From c91912700d31f1f3b54192beefc6f5496804a396 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 24 Mar 2009 10:34:30 -0500 Subject: [PATCH 001/779] just kill brittle test --- .../test/controller/session/cookie_store_test.rb | 21 -------------------- 1 files changed, 0 insertions(+), 21 deletions(-) diff --git a/actionpack/test/controller/session/cookie_store_test.rb b/actionpack/test/controller/session/cookie_store_test.rb index 48a961c..40fcd56 100644 --- a/actionpack/test/controller/session/cookie_store_test.rb +++ b/actionpack/test/controller/session/cookie_store_test.rb @@ -193,27 +193,6 @@ class CookieStoreTest < ActionController::IntegrationTest end end - def test_session_store_with_expire_after - app = ActionController::Session::CookieStore.new(DispatcherApp, :key => SessionKey, :secret => SessionSecret, :expire_after => 5.hours) - @integration_session = open_session(app) - - with_test_route_set do - # First request accesses the session - cookies[SessionKey] = SignedBar - - get '/set_session_value' - assert_response :success - cookie = headers['Set-Cookie'] - - # Second request does not access the session so the - # expires header should not be changed - get '/no_session_access' - assert_response :success - assert_equal cookie, headers['Set-Cookie'], - "#{unmarshal_session(cookie).inspect} expected but was #{unmarshal_session(headers['Set-Cookie']).inspect}" - end - end - private def with_test_route_set with_routing do |set| -- 1.6.4.2 From daffa5cbddace5a68ce09808a827805ef81ddb84 Mon Sep 17 00:00:00 2001 From: Peter Marklund Date: Tue, 24 Mar 2009 10:37:57 -0500 Subject: [PATCH 002/779] Reset request_parameters in TestRequest#recycle! to avoid multiple posts clobbering each other [#2271 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/test_process.rb | 1 + actionpack/test/controller/test_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index dbaec00..c5081a2 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -110,6 +110,7 @@ module ActionController #:nodoc: end def recycle! + @env["action_controller.request.request_parameters"] = {} self.query_parameters = {} self.path_parameters = {} @headers, @request_method, @accepts, @content_type = nil, nil, nil, nil diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 65c894c..3924b28 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -515,6 +515,14 @@ XML assert_nil @request.instance_variable_get("@request_method") end + def test_params_reset_after_post_request + post :no_op, :foo => "bar" + assert_equal "bar", @request.params[:foo] + @request.recycle! + post :no_op + assert @request.params[:foo].blank? + end + %w(controller response request).each do |variable| %w(get post put delete head process).each do |method| define_method("test_#{variable}_missing_for_#{method}_raises_error") do -- 1.6.4.2 From dace54b2e9bcdb49ffa43b204c3a8959c55f1276 Mon Sep 17 00:00:00 2001 From: David Dollar Date: Tue, 24 Mar 2009 10:41:45 -0500 Subject: [PATCH 003/779] Updates tests to cause the tests for the Request class not to proxy through a fake TestRequest object [#2278 state:resolved] Signed-off-by: Joshua Peek --- actionpack/test/controller/request_test.rb | 448 ++++++++++++++-------------- 1 files changed, 218 insertions(+), 230 deletions(-) diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/controller/request_test.rb index c72f885..c4cc63e 100644 --- a/actionpack/test/controller/request_test.rb +++ b/actionpack/test/controller/request_test.rb @@ -3,7 +3,6 @@ require 'abstract_unit' class RequestTest < ActiveSupport::TestCase def setup ActionController::Base.relative_url_root = nil - @request = ActionController::TestRequest.new end def teardown @@ -11,60 +10,52 @@ class RequestTest < ActiveSupport::TestCase end def test_remote_ip - assert_equal '0.0.0.0', @request.remote_ip + request = stub_request 'REMOTE_ADDR' => '1.2.3.4' + assert_equal '1.2.3.4', request.remote_ip - @request.remote_addr = '1.2.3.4' - assert_equal '1.2.3.4', @request.remote_ip + request = stub_request 'REMOTE_ADDR' => '1.2.3.4,3.4.5.6' + assert_equal '1.2.3.4', request.remote_ip - @request.remote_addr = '1.2.3.4,3.4.5.6' - assert_equal '1.2.3.4', @request.remote_ip + request = stub_request 'REMOTE_ADDR' => '1.2.3.4', + 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' + assert_equal '1.2.3.4', request.remote_ip - @request.env['HTTP_CLIENT_IP'] = '2.3.4.5' - assert_equal '1.2.3.4', @request.remote_ip + request = stub_request 'REMOTE_ADDR' => '127.0.0.1', + 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip - @request.remote_addr = '192.168.0.1' - assert_equal '2.3.4.5', @request.remote_ip - @request.env.delete 'HTTP_CLIENT_IP' + request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip - @request.remote_addr = '1.2.3.4' - @request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6' - assert_equal '1.2.3.4', @request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => '172.16.0.1,3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip - @request.remote_addr = '127.0.0.1' - @request.env['HTTP_X_FORWARDED_FOR'] = '3.4.5.6' - assert_equal '3.4.5.6', @request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => '192.168.0.1,3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip - @request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,3.4.5.6' - assert_equal '3.4.5.6', @request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => '10.0.0.1,3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip - @request.env['HTTP_X_FORWARDED_FOR'] = '172.16.0.1,3.4.5.6' - assert_equal '3.4.5.6', @request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 10.0.0.1, 3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip - @request.env['HTTP_X_FORWARDED_FOR'] = '192.168.0.1,3.4.5.6' - assert_equal '3.4.5.6', @request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => '127.0.0.1,3.4.5.6' + assert_equal '3.4.5.6', request.remote_ip - @request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1,3.4.5.6' - assert_equal '3.4.5.6', @request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1' + assert_equal 'unknown', request.remote_ip - @request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1, 10.0.0.1, 3.4.5.6' - assert_equal '3.4.5.6', @request.remote_ip + request = stub_request 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4' + assert_equal '3.4.5.6', request.remote_ip - @request.env['HTTP_X_FORWARDED_FOR'] = '127.0.0.1,3.4.5.6' - assert_equal '3.4.5.6', @request.remote_ip - - @request.env['HTTP_X_FORWARDED_FOR'] = 'unknown,192.168.0.1' - assert_equal 'unknown', @request.remote_ip - - @request.env['HTTP_X_FORWARDED_FOR'] = '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4' - assert_equal '3.4.5.6', @request.remote_ip - - @request.env['HTTP_CLIENT_IP'] = '8.8.8.8' + request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1', + 'HTTP_CLIENT_IP' => '2.2.2.2' e = assert_raise(ActionController::ActionControllerError) { - @request.remote_ip + request.remote_ip } assert_match /IP spoofing attack/, e.message - assert_match /HTTP_X_FORWARDED_FOR="9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4"/, e.message - assert_match /HTTP_CLIENT_IP="8.8.8.8"/, e.message + assert_match /HTTP_X_FORWARDED_FOR="1.1.1.1"/, e.message + assert_match /HTTP_CLIENT_IP="2.2.2.2"/, e.message # turn IP Spoofing detection off. # This is useful for sites that are aimed at non-IP clients. The typical @@ -72,336 +63,333 @@ class RequestTest < ActiveSupport::TestCase # leap of faith to assume that their proxies are ever going to set the # HTTP_CLIENT_IP/HTTP_X_FORWARDED_FOR headers properly. ActionController::Base.ip_spoofing_check = false - assert_equal('8.8.8.8', @request.remote_ip) + request = stub_request 'HTTP_X_FORWARDED_FOR' => '1.1.1.1', + 'HTTP_CLIENT_IP' => '2.2.2.2' + assert_equal '2.2.2.2', request.remote_ip ActionController::Base.ip_spoofing_check = true - @request.env['HTTP_X_FORWARDED_FOR'] = '8.8.8.8, 9.9.9.9' - assert_equal '8.8.8.8', @request.remote_ip - - @request.env.delete 'HTTP_CLIENT_IP' - @request.env.delete 'HTTP_X_FORWARDED_FOR' + request = stub_request 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9' + assert_equal '9.9.9.9', request.remote_ip end def test_domains - @request.host = "www.rubyonrails.org" - assert_equal "rubyonrails.org", @request.domain - - @request.host = "www.rubyonrails.co.uk" - assert_equal "rubyonrails.co.uk", @request.domain(2) + request = stub_request 'HTTP_HOST' => 'www.rubyonrails.org' + assert_equal "rubyonrails.org", request.domain - @request.host = "192.168.1.200" - assert_nil @request.domain + request = stub_request 'HTTP_HOST' => "www.rubyonrails.co.uk" + assert_equal "rubyonrails.co.uk", request.domain(2) - @request.host = "foo.192.168.1.200" - assert_nil @request.domain + request = stub_request 'HTTP_HOST' => "192.168.1.200" + assert_nil request.domain - @request.host = "192.168.1.200.com" - assert_equal "200.com", @request.domain + request = stub_request 'HTTP_HOST' => "foo.192.168.1.200" + assert_nil request.domain - @request.host = nil - assert_nil @request.domain + request = stub_request 'HTTP_HOST' => "192.168.1.200.com" + assert_equal "200.com", request.domain end def test_subdomains - @request.host = "www.rubyonrails.org" - assert_equal %w( www ), @request.subdomains + request = stub_request 'HTTP_HOST' => "www.rubyonrails.org" + assert_equal %w( www ), request.subdomains - @request.host = "www.rubyonrails.co.uk" - assert_equal %w( www ), @request.subdomains(2) + request = stub_request 'HTTP_HOST' => "www.rubyonrails.co.uk" + assert_equal %w( www ), request.subdomains(2) - @request.host = "dev.www.rubyonrails.co.uk" - assert_equal %w( dev www ), @request.subdomains(2) + request = stub_request 'HTTP_HOST' => "dev.www.rubyonrails.co.uk" + assert_equal %w( dev www ), request.subdomains(2) - @request.host = "foobar.foobar.com" - assert_equal %w( foobar ), @request.subdomains + request = stub_request 'HTTP_HOST' => "foobar.foobar.com" + assert_equal %w( foobar ), request.subdomains - @request.host = "192.168.1.200" - assert_equal [], @request.subdomains + request = stub_request 'HTTP_HOST' => "192.168.1.200" + assert_equal [], request.subdomains - @request.host = "foo.192.168.1.200" - assert_equal [], @request.subdomains + request = stub_request 'HTTP_HOST' => "foo.192.168.1.200" + assert_equal [], request.subdomains - @request.host = "192.168.1.200.com" - assert_equal %w( 192 168 1 ), @request.subdomains + request = stub_request 'HTTP_HOST' => "192.168.1.200.com" + assert_equal %w( 192 168 1 ), request.subdomains - @request.host = nil - assert_equal [], @request.subdomains + request = stub_request 'HTTP_HOST' => nil + assert_equal [], request.subdomains end def test_port_string - @request.port = 80 - assert_equal "", @request.port_string + request = stub_request 'HTTP_HOST' => 'www.example.org:80' + assert_equal "", request.port_string - @request.port = 8080 - assert_equal ":8080", @request.port_string + request = stub_request 'HTTP_HOST' => 'www.example.org:8080' + assert_equal ":8080", request.port_string end def test_request_uri - @request.env['SERVER_SOFTWARE'] = 'Apache 42.342.3432' + request = stub_request 'REQUEST_URI' => "http://www.rubyonrails.org/path/of/some/uri?mapped=1" + assert_equal "/path/of/some/uri?mapped=1", request.request_uri + assert_equal "/path/of/some/uri", request.path - @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri?mapped=1" - assert_equal "/path/of/some/uri?mapped=1", @request.request_uri - assert_equal "/path/of/some/uri", @request.path + request = stub_request 'REQUEST_URI' => "http://www.rubyonrails.org/path/of/some/uri" + assert_equal "/path/of/some/uri", request.request_uri + assert_equal "/path/of/some/uri", request.path - @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri" - assert_equal "/path/of/some/uri", @request.request_uri - assert_equal "/path/of/some/uri", @request.path + request = stub_request 'REQUEST_URI' => "/path/of/some/uri" + assert_equal "/path/of/some/uri", request.request_uri + assert_equal "/path/of/some/uri", request.path - @request.set_REQUEST_URI "/path/of/some/uri" - assert_equal "/path/of/some/uri", @request.request_uri - assert_equal "/path/of/some/uri", @request.path + request = stub_request 'REQUEST_URI' => "/" + assert_equal "/", request.request_uri + assert_equal "/", request.path - @request.set_REQUEST_URI "/" - assert_equal "/", @request.request_uri - assert_equal "/", @request.path + request = stub_request 'REQUEST_URI' => "/?m=b" + assert_equal "/?m=b", request.request_uri + assert_equal "/", request.path - @request.set_REQUEST_URI "/?m=b" - assert_equal "/?m=b", @request.request_uri - assert_equal "/", @request.path - - @request.set_REQUEST_URI "/" - @request.env['SCRIPT_NAME'] = "/dispatch.cgi" - assert_equal "/", @request.request_uri - assert_equal "/", @request.path + request = stub_request 'REQUEST_URI' => "/", 'SCRIPT_NAME' => '/dispatch.cgi' + assert_equal "/", request.request_uri + assert_equal "/", request.path ActionController::Base.relative_url_root = "/hieraki" - @request.set_REQUEST_URI "/hieraki/" - @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi" - assert_equal "/hieraki/", @request.request_uri - assert_equal "/", @request.path + request = stub_request 'REQUEST_URI' => "/hieraki/", 'SCRIPT_NAME' => "/hieraki/dispatch.cgi" + assert_equal "/hieraki/", request.request_uri + assert_equal "/", request.path ActionController::Base.relative_url_root = nil ActionController::Base.relative_url_root = "/collaboration/hieraki" - @request.set_REQUEST_URI "/collaboration/hieraki/books/edit/2" - @request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi" - assert_equal "/collaboration/hieraki/books/edit/2", @request.request_uri - assert_equal "/books/edit/2", @request.path + request = stub_request 'REQUEST_URI' => "/collaboration/hieraki/books/edit/2", + 'SCRIPT_NAME' => "/collaboration/hieraki/dispatch.cgi" + assert_equal "/collaboration/hieraki/books/edit/2", request.request_uri + assert_equal "/books/edit/2", request.path ActionController::Base.relative_url_root = nil # The following tests are for when REQUEST_URI is not supplied (as in IIS) - @request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1" - @request.env['SCRIPT_NAME'] = nil #"/path/dispatch.rb" - @request.set_REQUEST_URI nil - assert_equal "/path/of/some/uri?mapped=1", @request.request_uri - assert_equal "/path/of/some/uri", @request.path + request = stub_request 'PATH_INFO' => "/path/of/some/uri?mapped=1", + 'SCRIPT_NAME' => nil, + 'REQUEST_URI' => nil + assert_equal "/path/of/some/uri?mapped=1", request.request_uri + assert_equal "/path/of/some/uri", request.path ActionController::Base.relative_url_root = '/path' - @request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1" - @request.env['SCRIPT_NAME'] = "/path/dispatch.rb" - @request.set_REQUEST_URI nil - assert_equal "/path/of/some/uri?mapped=1", @request.request_uri - assert_equal "/of/some/uri", @request.path + request = stub_request 'PATH_INFO' => "/path/of/some/uri?mapped=1", + 'SCRIPT_NAME' => "/path/dispatch.rb", + 'REQUEST_URI' => nil + assert_equal "/path/of/some/uri?mapped=1", request.request_uri + assert_equal "/of/some/uri", request.path ActionController::Base.relative_url_root = nil - @request.env['PATH_INFO'] = "/path/of/some/uri" - @request.env['SCRIPT_NAME'] = nil - @request.set_REQUEST_URI nil - assert_equal "/path/of/some/uri", @request.request_uri - assert_equal "/path/of/some/uri", @request.path + request = stub_request 'PATH_INFO' => "/path/of/some/uri", + 'SCRIPT_NAME' => nil, + 'REQUEST_URI' => nil + assert_equal "/path/of/some/uri", request.request_uri + assert_equal "/path/of/some/uri", request.path - @request.env['PATH_INFO'] = "/" - @request.set_REQUEST_URI nil - assert_equal "/", @request.request_uri - assert_equal "/", @request.path + request = stub_request 'PATH_INFO' => '/', 'REQUEST_URI' => nil + assert_equal "/", request.request_uri + assert_equal "/", request.path - @request.env['PATH_INFO'] = "/?m=b" - @request.set_REQUEST_URI nil - assert_equal "/?m=b", @request.request_uri - assert_equal "/", @request.path + request = stub_request 'PATH_INFO' => '/?m=b', 'REQUEST_URI' => nil + assert_equal "/?m=b", request.request_uri + assert_equal "/", request.path - @request.env['PATH_INFO'] = "/" - @request.env['SCRIPT_NAME'] = "/dispatch.cgi" - @request.set_REQUEST_URI nil - assert_equal "/", @request.request_uri - assert_equal "/", @request.path + request = stub_request 'PATH_INFO' => "/", + 'SCRIPT_NAME' => "/dispatch.cgi", + 'REQUEST_URI' => nil + assert_equal "/", request.request_uri + assert_equal "/", request.path ActionController::Base.relative_url_root = '/hieraki' - @request.env['PATH_INFO'] = "/hieraki/" - @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi" - @request.set_REQUEST_URI nil - assert_equal "/hieraki/", @request.request_uri - assert_equal "/", @request.path + request = stub_request 'PATH_INFO' => "/hieraki/", + 'SCRIPT_NAME' => "/hieraki/dispatch.cgi", + 'REQUEST_URI' => nil + assert_equal "/hieraki/", request.request_uri + assert_equal "/", request.path ActionController::Base.relative_url_root = nil - @request.set_REQUEST_URI '/hieraki/dispatch.cgi' + request = stub_request 'REQUEST_URI' => '/hieraki/dispatch.cgi' ActionController::Base.relative_url_root = '/hieraki' - assert_equal "/dispatch.cgi", @request.path + assert_equal "/dispatch.cgi", request.path ActionController::Base.relative_url_root = nil - @request.set_REQUEST_URI '/hieraki/dispatch.cgi' + request = stub_request 'REQUEST_URI' => '/hieraki/dispatch.cgi' ActionController::Base.relative_url_root = '/foo' - assert_equal "/hieraki/dispatch.cgi", @request.path + assert_equal "/hieraki/dispatch.cgi", request.path ActionController::Base.relative_url_root = nil # This test ensures that Rails uses REQUEST_URI over PATH_INFO ActionController::Base.relative_url_root = nil - @request.env['REQUEST_URI'] = "/some/path" - @request.env['PATH_INFO'] = "/another/path" - @request.env['SCRIPT_NAME'] = "/dispatch.cgi" - assert_equal "/some/path", @request.request_uri - assert_equal "/some/path", @request.path + request = stub_request 'REQUEST_URI' => "/some/path", + 'PATH_INFO' => "/another/path", + 'SCRIPT_NAME' => "/dispatch.cgi" + assert_equal "/some/path", request.request_uri + assert_equal "/some/path", request.path end def test_host_with_default_port - @request.host = "rubyonrails.org" - @request.port = 80 - assert_equal "rubyonrails.org", @request.host_with_port + request = stub_request 'HTTP_HOST' => 'rubyonrails.org:80' + assert_equal "rubyonrails.org", request.host_with_port end def test_host_with_non_default_port - @request.host = "rubyonrails.org" - @request.port = 81 - assert_equal "rubyonrails.org:81", @request.host_with_port + request = stub_request 'HTTP_HOST' => 'rubyonrails.org:81' + assert_equal "rubyonrails.org:81", request.host_with_port end def test_server_software - assert_equal nil, @request.server_software + request = stub_request + assert_equal nil, request.server_software - @request.env['SERVER_SOFTWARE'] = 'Apache3.422' - assert_equal 'apache', @request.server_software + request = stub_request 'SERVER_SOFTWARE' => 'Apache3.422' + assert_equal 'apache', request.server_software - @request.env['SERVER_SOFTWARE'] = 'lighttpd(1.1.4)' - assert_equal 'lighttpd', @request.server_software + request = stub_request 'SERVER_SOFTWARE' => 'lighttpd(1.1.4)' + assert_equal 'lighttpd', request.server_software end def test_xml_http_request - assert !@request.xml_http_request? - assert !@request.xhr? + request = stub_request + + assert !request.xml_http_request? + assert !request.xhr? - @request.env['HTTP_X_REQUESTED_WITH'] = "DefinitelyNotAjax1.0" - assert !@request.xml_http_request? - assert !@request.xhr? + request = stub_request 'HTTP_X_REQUESTED_WITH' => 'DefinitelyNotAjax1.0' + assert !request.xml_http_request? + assert !request.xhr? - @request.env['HTTP_X_REQUESTED_WITH'] = "XMLHttpRequest" - assert @request.xml_http_request? - assert @request.xhr? + request = stub_request 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest' + assert request.xml_http_request? + assert request.xhr? end def test_reports_ssl - assert !@request.ssl? - @request.env['HTTPS'] = 'on' - assert @request.ssl? + request = stub_request + assert !request.ssl? + + request = stub_request 'HTTPS' => 'on' + assert request.ssl? end def test_reports_ssl_when_proxied_via_lighttpd - assert !@request.ssl? - @request.env['HTTP_X_FORWARDED_PROTO'] = 'https' - assert @request.ssl? + request = stub_request + assert !request.ssl? + + request = stub_request 'HTTP_X_FORWARDED_PROTO' => 'https' + assert request.ssl? end def test_symbolized_request_methods [:get, :post, :put, :delete].each do |method| - self.request_method = method - assert_equal method, @request.method + request = stub_request 'REQUEST_METHOD' => method.to_s.upcase + assert_equal method, request.method end end def test_invalid_http_method_raises_exception assert_raise(ActionController::UnknownHttpMethod) do - self.request_method = :random_method - @request.request_method + request = stub_request 'REQUEST_METHOD' => 'RANDOM_METHOD' + request.request_method end end def test_allow_method_hacking_on_post [:get, :head, :options, :put, :post, :delete].each do |method| - self.request_method = method - assert_equal(method == :head ? :get : method, @request.method) - end - end - - def test_invalid_method_hacking_on_post_raises_exception - assert_raise(ActionController::UnknownHttpMethod) do - self.request_method = :_random_method - @request.request_method + request = stub_request 'REQUEST_METHOD' => method.to_s.upcase + assert_equal(method == :head ? :get : method, request.method) end end def test_restrict_method_hacking - @request.instance_eval { @parameters = { :_method => 'put' } } [:get, :put, :delete].each do |method| - self.request_method = method - assert_equal method, @request.method + request = stub_request 'REQUEST_METHOD' => method.to_s.upcase, + 'action_controller.request.request_parameters' => { :_method => 'put' } + assert_equal method, request.method end end def test_head_masquerading_as_get - self.request_method = :head - assert_equal :get, @request.method - assert @request.get? - assert @request.head? + request = stub_request 'REQUEST_METHOD' => 'HEAD' + assert_equal :get, request.method + assert request.get? + assert request.head? end def test_xml_format - @request.instance_eval { @parameters = { :format => 'xml' } } - assert_equal Mime::XML, @request.format + request = stub_request + request.expects(:parameters).at_least_once.returns({ :format => 'xml' }) + assert_equal Mime::XML, request.format end def test_xhtml_format - @request.instance_eval { @parameters = { :format => 'xhtml' } } - assert_equal Mime::HTML, @request.format + request = stub_request + request.expects(:parameters).at_least_once.returns({ :format => 'xhtml' }) + assert_equal Mime::HTML, request.format end def test_txt_format - @request.instance_eval { @parameters = { :format => 'txt' } } - assert_equal Mime::TEXT, @request.format + request = stub_request + request.expects(:parameters).at_least_once.returns({ :format => 'txt' }) + assert_equal Mime::TEXT, request.format end - def test_nil_format + def test_xml_http_request ActionController::Base.use_accept_header, old = false, ActionController::Base.use_accept_header - @request.instance_eval { @parameters = {} } - @request.env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" - assert @request.xhr? - assert_equal Mime::JS, @request.format - + request = stub_request 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest' + request.expects(:parameters).at_least_once.returns({}) + assert request.xhr? + assert_equal Mime::JS, request.format ensure ActionController::Base.use_accept_header = old end def test_content_type - @request.env["CONTENT_TYPE"] = "text/html" - assert_equal Mime::HTML, @request.content_type + request = stub_request 'CONTENT_TYPE' => 'text/html' + assert_equal Mime::HTML, request.content_type end - def test_format_assignment_should_set_format - @request.instance_eval { self.format = :txt } - assert !@request.format.xml? - @request.instance_eval { self.format = :xml } - assert @request.format.xml? + def test_can_override_format_with_parameter + request = stub_request + request.expects(:parameters).at_least_once.returns({ :format => :txt }) + assert !request.format.xml? + + request = stub_request + request.expects(:parameters).at_least_once.returns({ :format => :xml }) + assert request.format.xml? end def test_content_no_type - assert_equal nil, @request.content_type + request = stub_request + assert_equal nil, request.content_type end def test_content_type_xml - @request.env["CONTENT_TYPE"] = "application/xml" - assert_equal Mime::XML, @request.content_type + request = stub_request 'CONTENT_TYPE' => 'application/xml' + assert_equal Mime::XML, request.content_type end def test_content_type_with_charset - @request.env["CONTENT_TYPE"] = "application/xml; charset=UTF-8" - assert_equal Mime::XML, @request.content_type + request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8' + assert_equal Mime::XML, request.content_type end def test_user_agent - assert_not_nil @request.user_agent + request = stub_request 'HTTP_USER_AGENT' => 'TestAgent' + assert_equal 'TestAgent', request.user_agent end def test_parameters - @request.stubs(:request_parameters).returns({ "foo" => 1 }) - @request.stubs(:query_parameters).returns({ "bar" => 2 }) + request = stub_request + request.stubs(:request_parameters).returns({ "foo" => 1 }) + request.stubs(:query_parameters).returns({ "bar" => 2 }) - assert_equal({"foo" => 1, "bar" => 2}, @request.parameters) - assert_equal({"foo" => 1}, @request.request_parameters) - assert_equal({"bar" => 2}, @request.query_parameters) + assert_equal({"foo" => 1, "bar" => 2}, request.parameters) + assert_equal({"foo" => 1}, request.request_parameters) + assert_equal({"bar" => 2}, request.query_parameters) + end + +protected + + def stub_request(env={}) + ActionController::Request.new(env) end - protected - def request_method=(method) - @request.env['REQUEST_METHOD'] = method.to_s.upcase - @request.request_method = nil # Reset the ivar cache - end end -- 1.6.4.2 From d2e6a0fbc30d3fd03e7945306d18ed8ced8b20cf Mon Sep 17 00:00:00 2001 From: thedarkone Date: Tue, 24 Mar 2009 10:44:54 -0500 Subject: [PATCH 004/779] Simplify handling of absolute path templates. [#2276 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_view/paths.rb | 2 +- actionpack/lib/action_view/template.rb | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 8cc3fe2..a0a2f96 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -61,7 +61,7 @@ module ActionView #:nodoc: end end - return Template.new(original_template_path, original_template_path.to_s =~ /\A\// ? "" : ".") if File.file?(original_template_path) + return Template.new(original_template_path) if File.file?(original_template_path) raise MissingTemplate.new(self, original_template_path, format) end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index c339c8a..4497c4a 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -107,9 +107,8 @@ module ActionView #:nodoc: attr_accessor :locale, :name, :format, :extension delegate :to_s, :to => :path - def initialize(template_path, load_path) - @template_path = template_path.dup - @load_path, @filename = load_path, File.join(load_path, template_path) + def initialize(template_path, load_path = nil) + @template_path, @load_path = template_path.dup, load_path @base_path, @name, @locale, @format, @extension = split(template_path) @base_path.to_s.gsub!(/\/$/, '') # Push to split method @@ -180,6 +179,12 @@ module ActionView #:nodoc: @@exempt_from_layout.any? { |exempted| path =~ exempted } end + def filename + # no load_path means this is an "absolute pathed" template + load_path ? File.join(load_path, template_path) : template_path + end + memoize :filename + def source File.read(filename) end -- 1.6.4.2 From 6a1267a0b12560a752ebfb443194de1a0ab3bef5 Mon Sep 17 00:00:00 2001 From: thedarkone Date: Tue, 24 Mar 2009 10:48:47 -0500 Subject: [PATCH 005/779] Fix template extension parsing. [#2315 state:resolved] [#2284 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_view/template.rb | 44 +++++++++------------------- actionpack/test/template/template_test.rb | 32 +++++++++++++++++++++ 2 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 actionpack/test/template/template_test.rb diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 4497c4a..a974f26 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -217,46 +217,30 @@ module ActionView #:nodoc: end def valid_locale?(locale) - I18n.available_locales.include?(locale.to_sym) + locale && I18n.available_locales.include?(locale.to_sym) end # Returns file split into an array # [base_path, name, locale, format, extension] def split(file) if m = file.to_s.match(/^(.*\/)?([^\.]+)\.(.*)$/) - base_path = m[1] - name = m[2] - extensions = m[3] - else - return + [m[1], m[2], *parse_extensions(m[3])] end + end - locale = nil - format = nil - extension = nil - - if m = extensions.split(".") - if valid_locale?(m[0]) && m[1] && valid_extension?(m[2]) # All three - locale = m[0] - format = m[1] - extension = m[2] - elsif m[0] && m[1] && valid_extension?(m[2]) # Multipart formats - format = "#{m[0]}.#{m[1]}" - extension = m[2] - elsif valid_locale?(m[0]) && valid_extension?(m[1]) # locale and extension - locale = m[0] - extension = m[1] - elsif valid_extension?(m[1]) # format and extension - format = m[0] - extension = m[1] - elsif valid_extension?(m[0]) # Just extension - extension = m[0] - else # No extension - format = m[0] - end + # returns parsed extensions as an array + # [locale, format, extension] + def parse_extensions(extensions) + exts = extensions.split(".") + + if extension = valid_extension?(exts.last) && exts.pop || nil + locale = valid_locale?(exts.first) && exts.shift || nil + format = exts.join('.') if exts.any? # join('.') is needed for multipart templates + else # no extension, just format + format = exts.last end - [base_path, name, locale, format, extension] + [locale, format, extension] end end end diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb new file mode 100644 index 0000000..7caec7a --- /dev/null +++ b/actionpack/test/template/template_test.rb @@ -0,0 +1,32 @@ +require 'abstract_unit' + +class TemplateTest < Test::Unit::TestCase + def test_template_path_parsing + with_options :base_path => nil, :name => 'abc', :locale => nil, :format => 'html', :extension => 'erb' do |t| + t.assert_parses_template_path 'abc.en.html.erb', :locale => 'en' + t.assert_parses_template_path 'abc.en.plain.html.erb', :locale => 'en', :format => 'plain.html' + t.assert_parses_template_path 'abc.html.erb' + t.assert_parses_template_path 'abc.plain.html.erb', :format => 'plain.html' + t.assert_parses_template_path 'abc.erb', :format => nil + t.assert_parses_template_path 'abc.html', :extension => nil + + t.assert_parses_template_path '_abc.html.erb', :name => '_abc' + + t.assert_parses_template_path 'test/abc.html.erb', :base_path => 'test' + t.assert_parses_template_path './test/abc.html.erb', :base_path => './test' + t.assert_parses_template_path '../test/abc.html.erb', :base_path => '../test' + + t.assert_parses_template_path 'abc', :extension => nil, :format => nil, :name => nil + t.assert_parses_template_path 'abc.xxx', :extension => nil, :format => 'xxx', :name => 'abc' + t.assert_parses_template_path 'abc.html.xxx', :extension => nil, :format => 'xxx', :name => 'abc' + end + end + + private + def assert_parses_template_path(path, parse_results) + template = ActionView::Template.new(path, '') + parse_results.each_pair do |k, v| + assert_block(%Q{Expected template to parse #{k.inspect} from "#{path}" as #{v.inspect}, but got #{template.send(k).inspect}}) { v == template.send(k) } + end + end +end -- 1.6.4.2 From 651611999df3e57de6f36486b51abd3bf5d66cea Mon Sep 17 00:00:00 2001 From: Ryan Angilly Date: Tue, 24 Mar 2009 10:51:45 -0500 Subject: [PATCH 006/779] adding session_options initialization and test [#2303 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/test_process.rb | 1 + actionpack/test/controller/test_test.rb | 4 ++++ 2 files changed, 5 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index c5081a2..03c4d4f 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -13,6 +13,7 @@ module ActionController #:nodoc: @query_parameters = {} @session = TestSession.new + @session_options ||= {} initialize_default_values initialize_containers diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 3924b28..6bf8a10 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -130,6 +130,10 @@ XML ActionController::Routing::Routes.reload end + def test_test_request_has_session_options_initialized + assert @request.session_options + end + def test_raw_post_handling params = {:page => {:name => 'page name'}, 'some key' => 123} post :render_raw_post, params.dup -- 1.6.4.2 From ace154d067f619aca8a56988efdf9477898242ae Mon Sep 17 00:00:00 2001 From: Kenny Ortmann Date: Tue, 7 Apr 2009 09:18:42 -0500 Subject: [PATCH 007/779] added tests for session options being defaulted correctly to rack defaults [#2403 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/test_process.rb | 8 ++++- .../test/controller/request/test_request_test.rb | 35 ++++++++++++++++++++ actionpack/test/controller/test_test.rb | 4 -- 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 actionpack/test/controller/request/test_request_test.rb diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 03c4d4f..e9bab44 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -1,3 +1,4 @@ +require 'rack/session/abstract/id' module ActionController #:nodoc: class TestRequest < Request #:nodoc: attr_accessor :cookies, :session_options @@ -13,7 +14,8 @@ module ActionController #:nodoc: @query_parameters = {} @session = TestSession.new - @session_options ||= {} + default_rack_options = Rack::Session::Abstract::ID::DEFAULT_OPTIONS + @session_options ||= {:id => generate_sid(default_rack_options[:sidbits])}.merge(default_rack_options) initialize_default_values initialize_containers @@ -122,6 +124,10 @@ module ActionController #:nodoc: end private + def generate_sid(sidbits) + "%0#{sidbits / 4}x" % rand(2**sidbits - 1) + end + def initialize_containers @cookies = {} end diff --git a/actionpack/test/controller/request/test_request_test.rb b/actionpack/test/controller/request/test_request_test.rb new file mode 100644 index 0000000..81551b4 --- /dev/null +++ b/actionpack/test/controller/request/test_request_test.rb @@ -0,0 +1,35 @@ +require 'abstract_unit' +require 'stringio' + +class ActionController::TestRequestTest < ActiveSupport::TestCase + + def setup + @request = ActionController::TestRequest.new + end + + def test_test_request_has_session_options_initialized + assert @request.session_options + end + + Rack::Session::Abstract::ID::DEFAULT_OPTIONS.each_key do |option| + test "test_rack_default_session_options_#{option}_exists_in_session_options_and_is_default" do + assert_equal(Rack::Session::Abstract::ID::DEFAULT_OPTIONS[option], + @request.session_options[option], + "Missing rack session default option #{option} in request.session_options") + end + test "test_rack_default_session_options_#{option}_exists_in_session_options" do + assert(@request.session_options.has_key?(option), + "Missing rack session option #{option} in request.session_options") + end + end + + def test_session_id_exists_by_default + assert_not_nil(@request.session_options[:id]) + end + + def test_session_id_different_on_each_call + prev_id = + assert_not_equal(@request.session_options[:id], ActionController::TestRequest.new.session_options[:id]) + end + +end \ No newline at end of file diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 6bf8a10..3924b28 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -130,10 +130,6 @@ XML ActionController::Routing::Routes.reload end - def test_test_request_has_session_options_initialized - assert @request.session_options - end - def test_raw_post_handling params = {:page => {:name => 'page name'}, 'some key' => 123} post :render_raw_post, params.dup -- 1.6.4.2 From dc69d9308aefb249eae3776e186f4c94fdf50eb7 Mon Sep 17 00:00:00 2001 From: Doug McInnes Date: Tue, 3 Feb 2009 18:37:55 -0800 Subject: [PATCH 008/779] Fix for TestResponse.cookies returning cookies unescaped [#1867 state:resolved] Signed-off-by: David Heinemeier Hansson --- actionpack/CHANGELOG | 5 +++++ actionpack/lib/action_controller/test_process.rb | 2 +- actionpack/test/controller/cookie_test.rb | 10 ++++++++++ 3 files changed, 16 insertions(+), 1 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 8c9486c..11ee1c1 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,8 @@ +*Edge* + +* Fixed that TestResponse.cookies was returning cookies unescaped #1867 [Doug McInnes] + + *2.3.2 [Final] (March 15, 2009)* * Fixed that redirection would just log the options, not the final url (which lead to "Redirected to #") [DHH] diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index e9bab44..9de3fab 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -258,7 +258,7 @@ module ActionController #:nodoc: def cookies cookies = {} Array(headers['Set-Cookie']).each do |cookie| - key, value = cookie.split(";").first.split("=") + key, value = cookie.split(";").first.split("=").map {|val| Rack::Utils.unescape(val)} cookies[key] = value end cookies diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb index 657be3c..f7d97e1 100644 --- a/actionpack/test/controller/cookie_test.rb +++ b/actionpack/test/controller/cookie_test.rb @@ -6,6 +6,10 @@ class CookieTest < ActionController::TestCase cookies["user_name"] = "david" end + def set_with_with_escapable_characters + cookies["that & guy"] = "foo & bar => baz" + end + def authenticate_for_fourteen_days cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) } end @@ -53,6 +57,12 @@ class CookieTest < ActionController::TestCase 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_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"] -- 1.6.4.2 From 50e867480a265b9ef60401ab0d394d9baeb5b95e Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 16 Apr 2009 16:48:07 -0500 Subject: [PATCH 009/779] Added ActiveRecord::Base#touch to update the updated_at/on attributes with the current time [DHH] --- activerecord/CHANGELOG | 5 +++ activerecord/lib/active_record/timestamp.rb | 38 ++++++++++++++++++++------ activerecord/test/cases/timestamp_test.rb | 30 +++++++++++++++++++++ 3 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 activerecord/test/cases/timestamp_test.rb diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index c73ac46..472d4aa 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,3 +1,8 @@ +*Edge* + +* Added ActiveRecord::Base#touch to update the updated_at/on attributes with the current time [DHH] + + *2.3.2 [Final] (March 15, 2009)* * Added ActiveRecord::Base.find_each and ActiveRecord::Base.find_in_batches for batch processing [DHH/Jamis Buck] diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 8dbe80a..648861f 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -15,27 +15,47 @@ module ActiveRecord base.class_inheritable_accessor :record_timestamps, :instance_writer => false base.record_timestamps = true end + + # Saves the record with the updated_at/on attributes set to the current time. + # If the save fails because of validation errors, an ActiveRecord::RecordInvalid exception is raised. + def touch + current_time = current_time_from_proper_timezone + + write_attribute('updated_at', current_time) if respond_to?(:updated_at) + write_attribute('updated_on', current_time) if respond_to?(:updated_on) + + save! + end + private def create_with_timestamps #:nodoc: if record_timestamps - t = self.class.default_timezone == :utc ? Time.now.utc : Time.now - write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil? - write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil? + current_time = current_time_from_proper_timezone + + write_attribute('created_at', current_time) if respond_to?(:created_at) && created_at.nil? + write_attribute('created_on', current_time) if respond_to?(:created_on) && created_on.nil? - write_attribute('updated_at', t) if respond_to?(:updated_at) && updated_at.nil? - write_attribute('updated_on', t) if respond_to?(:updated_on) && updated_on.nil? + write_attribute('updated_at', current_time) if respond_to?(:updated_at) && updated_at.nil? + write_attribute('updated_on', current_time) if respond_to?(:updated_on) && updated_on.nil? end + create_without_timestamps end def update_with_timestamps(*args) #:nodoc: if record_timestamps && (!partial_updates? || changed?) - t = self.class.default_timezone == :utc ? Time.now.utc : Time.now - write_attribute('updated_at', t) if respond_to?(:updated_at) - write_attribute('updated_on', t) if respond_to?(:updated_on) + current_time = current_time_from_proper_timezone + + write_attribute('updated_at', current_time) if respond_to?(:updated_at) + write_attribute('updated_on', current_time) if respond_to?(:updated_on) end + update_without_timestamps(*args) end + + def current_time_from_proper_timezone + self.class.default_timezone == :utc ? Time.now.utc : Time.now + end end -end +end \ No newline at end of file diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb new file mode 100644 index 0000000..e5f8fb9 --- /dev/null +++ b/activerecord/test/cases/timestamp_test.rb @@ -0,0 +1,30 @@ +require 'cases/helper' +require 'models/developer' + +class TimestampTest < ActiveRecord::TestCase + fixtures :developers + + def setup + @developer = Developer.first + @previously_updated_at = @developer.updated_at + end + + def test_saving_a_changed_record_updates_its_timestamp + @developer.name = "Jack Bauer" + @developer.save! + + assert @previously_updated_at != @developer.updated_at + end + + def test_saving_a_unchanged_record_doesnt_update_its_timestamp + @developer.save! + + assert @previously_updated_at == @developer.updated_at + end + + def test_touching_a_record_updates_its_timestamp + @developer.touch + + assert @previously_updated_at != @developer.updated_at + end +end \ No newline at end of file -- 1.6.4.2 From fa750e08a8d8c9f07afd88e616284549598926e2 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 16 Apr 2009 17:25:55 -0500 Subject: [PATCH 010/779] Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH] --- activerecord/CHANGELOG | 4 +- activerecord/lib/active_record/associations.rb | 68 ++++++++++++++++-------- activerecord/lib/active_record/timestamp.rb | 16 +++++- activerecord/test/cases/timestamp_test.rb | 47 ++++++++++++++++- activerecord/test/models/pet.rb | 2 +- activerecord/test/schema/schema.rb | 2 + 6 files changed, 110 insertions(+), 29 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 472d4aa..d58b441 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,6 +1,8 @@ *Edge* -* Added ActiveRecord::Base#touch to update the updated_at/on attributes with the current time [DHH] +* 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] *2.3.2 [Final] (March 15, 2009)* diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 6d25b36..53a7105 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -981,6 +981,9 @@ module ActiveRecord # If false, don't validate the associated objects when saving the parent object. +false+ by default. # [:autosave] # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default. + # [:touch] + # If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or + # destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute. # # Option examples: # belongs_to :firm, :foreign_key => "client_of" @@ -990,6 +993,8 @@ module ActiveRecord # belongs_to :attachable, :polymorphic => true # belongs_to :project, :readonly => true # belongs_to :post, :counter_cache => true + # belongs_to :company, :touch => true + # belongs_to :company, :touch => :employees_last_updated_at def belongs_to(association_id, options = {}) reflection = create_belongs_to_reflection(association_id, options) @@ -1001,28 +1006,8 @@ module ActiveRecord association_constructor_method(:create, reflection, BelongsToAssociation) end - # Create the callbacks to update counter cache - if options[:counter_cache] - cache_column = reflection.counter_cache_column - - method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym - define_method(method_name) do - association = send(reflection.name) - association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil? - end - after_create method_name - - method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym - define_method(method_name) do - association = send(reflection.name) - association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil? - end - before_destroy method_name - - module_eval( - "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)" - ) - end + add_counter_cache_callbacks(reflection) if options[:counter_cache] + add_touch_callbacks(reflection, options[:touch]) if options[:touch] configure_dependency_for_belongs_to(reflection) end @@ -1329,6 +1314,43 @@ module ActiveRecord end end + def add_counter_cache_callbacks(reflection) + cache_column = reflection.counter_cache_column + + method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym + define_method(method_name) do + association = send(reflection.name) + association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil? + end + after_create(method_name) + + method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym + define_method(method_name) do + association = send(reflection.name) + association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil? + end + before_destroy(method_name) + + module_eval( + "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)" + ) + end + + def add_touch_callbacks(reflection, touch_attribute) + method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym + define_method(method_name) do + association = send(reflection.name) + + if touch_attribute == true + association.touch unless association.nil? + else + association.touch(touch_attribute) unless association.nil? + end + end + after_save(method_name) + after_destroy(method_name) + end + def find_with_associations(options = {}) catch :invalid_query do join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins]) @@ -1499,7 +1521,7 @@ module ActiveRecord @@valid_keys_for_belongs_to_association = [ :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent, :counter_cache, :extend, :polymorphic, :readonly, - :validate + :validate, :touch ] def create_belongs_to_reflection(association_id, options) diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 648861f..d9e1ef3 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -18,11 +18,21 @@ module ActiveRecord # Saves the record with the updated_at/on attributes set to the current time. # If the save fails because of validation errors, an ActiveRecord::RecordInvalid exception is raised. - def touch + # If an attribute name is passed, that attribute is used for the touch instead of the updated_at/on attributes. + # + # Examples: + # + # product.touch # updates updated_at + # product.touch(:designed_at) # updates the designed_at attribute + def touch(attribute = nil) current_time = current_time_from_proper_timezone - write_attribute('updated_at', current_time) if respond_to?(:updated_at) - write_attribute('updated_on', current_time) if respond_to?(:updated_on) + if attribute + write_attribute(attribute, current_time) + else + write_attribute('updated_at', current_time) if respond_to?(:updated_at) + write_attribute('updated_on', current_time) if respond_to?(:updated_on) + end save! end diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index e5f8fb9..24b237a 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -1,8 +1,10 @@ require 'cases/helper' require 'models/developer' +require 'models/owner' +require 'models/pet' class TimestampTest < ActiveRecord::TestCase - fixtures :developers + fixtures :developers, :owners, :pets def setup @developer = Developer.first @@ -27,4 +29,47 @@ class TimestampTest < ActiveRecord::TestCase assert @previously_updated_at != @developer.updated_at end + + def test_touching_a_different_attribute + previously_created_at = @developer.created_at + @developer.touch(:created_at) + + assert previously_created_at != @developer.created_at + end + + def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at + pet = Pet.first + owner = pet.owner + previously_owner_updated_at = owner.updated_at + + pet.name = "Fluffy the Third" + pet.save + + assert previously_owner_updated_at != pet.owner.updated_at + end + + def test_destroying_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at + pet = Pet.first + owner = pet.owner + previously_owner_updated_at = owner.updated_at + + pet.destroy + + assert previously_owner_updated_at != pet.owner.updated_at + end + + def test_saving_a_record_with_a_belongs_to_that_specifies_touching_a_specific_attribute_the_parent_should_update_that_attribute + Pet.belongs_to :owner, :touch => :happy_at + + pet = Pet.first + owner = pet.owner + previously_owner_happy_at = owner.happy_at + + pet.name = "Fluffy the Third" + pet.save + + assert previously_owner_happy_at != pet.owner.happy_at + ensure + Pet.belongs_to :owner, :touch => true + end end \ No newline at end of file diff --git a/activerecord/test/models/pet.rb b/activerecord/test/models/pet.rb index dc1a3c5..a8bf94d 100644 --- a/activerecord/test/models/pet.rb +++ b/activerecord/test/models/pet.rb @@ -1,5 +1,5 @@ class Pet < ActiveRecord::Base set_primary_key :pet_id - belongs_to :owner + belongs_to :owner, :touch => true has_many :toys end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index ea848a2..5640510 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -281,6 +281,8 @@ ActiveRecord::Schema.define do create_table :owners, :primary_key => :owner_id ,:force => true do |t| t.string :name + t.column :updated_at, :datetime + t.column :happy_at, :datetime end -- 1.6.4.2 From c9a3d991640539375ba2bf8b6a0669a6c120df6d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 17 Apr 2009 13:44:59 -0500 Subject: [PATCH 011/779] Clearer String#first and #last edge cases. Fix that foo.first(0) == instead of foo. --- .../lib/active_support/core_ext/string/access.rb | 18 +++++++++++++++--- activesupport/test/core_ext/string_ext_test.rb | 2 ++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb index 7fb21fa..e067f93 100644 --- a/activesupport/lib/active_support/core_ext/string/access.rb +++ b/activesupport/lib/active_support/core_ext/string/access.rb @@ -41,9 +41,15 @@ module ActiveSupport #:nodoc: # "hello".first(2) # => "he" # "hello".first(10) # => "hello" def first(limit = 1) - mb_chars[0..(limit - 1)].to_s + if limit == 0 + '' + elsif limit >= size + self + else + mb_chars[0...limit].to_s + end end - + # Returns the last character of the string or the last +limit+ characters. # # Examples: @@ -51,7 +57,13 @@ module ActiveSupport #:nodoc: # "hello".last(2) # => "lo" # "hello".last(10) # => "hello" def last(limit = 1) - (mb_chars[(-limit)..-1] || self).to_s + if limit == 0 + '' + elsif limit >= size + self + else + mb_chars[(-limit)..-1].to_s + end end end else diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 6c9b7e7..7d51e81 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -132,10 +132,12 @@ class StringInflectionsTest < Test::Unit::TestCase assert_equal "h", s.first assert_equal "he", s.first(2) + assert_equal "", s.first(0) assert_equal "o", s.last assert_equal "llo", s.last(3) assert_equal "hello", s.last(10) + assert_equal "", s.last(0) assert_equal 'x', 'x'.first assert_equal 'x', 'x'.first(4) -- 1.6.4.2 From 878aec9d9567438ad414d33b9b63c44160602420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Fri, 17 Apr 2009 19:35:03 -0500 Subject: [PATCH 012/779] Improve rewindable input test coverage so tests fail when you remove the middleware Signed-off-by: Joshua Peek --- .../request/multipart_params_parsing_test.rb | 3 +-- .../request/url_encoded_params_parsing_test.rb | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/actionpack/test/controller/request/multipart_params_parsing_test.rb b/actionpack/test/controller/request/multipart_params_parsing_test.rb index b812072..7099fc4 100644 --- a/actionpack/test/controller/request/multipart_params_parsing_test.rb +++ b/actionpack/test/controller/request/multipart_params_parsing_test.rb @@ -206,8 +206,7 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest end def call(env) - req = Rack::Request.new(env) - req.params # Parse params + env['rack.input'].read @app.call(env) end end diff --git a/actionpack/test/controller/request/url_encoded_params_parsing_test.rb b/actionpack/test/controller/request/url_encoded_params_parsing_test.rb index 7e6099a..9936b77 100644 --- a/actionpack/test/controller/request/url_encoded_params_parsing_test.rb +++ b/actionpack/test/controller/request/url_encoded_params_parsing_test.rb @@ -150,8 +150,7 @@ class UrlEncodedParamsParsingTest < ActionController::IntegrationTest end def call(env) - req = Rack::Request.new(env) - req.params # Parse params + env['rack.input'].read @app.call(env) end end -- 1.6.4.2 From 35c5727acea882f4cef2a8a2d12d87a8fda045c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Fri, 17 Apr 2009 21:53:44 -0500 Subject: [PATCH 013/779] Always buffer rack.input if it is not rewindable Signed-off-by: Joshua Peek --- .../lib/action_controller/rewindable_input.rb | 25 ++++++------------- actionpack/test/controller/dispatcher_test.rb | 2 +- .../request/multipart_params_parsing_test.rb | 1 + .../request/url_encoded_params_parsing_test.rb | 1 + 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/actionpack/lib/action_controller/rewindable_input.rb b/actionpack/lib/action_controller/rewindable_input.rb index cedfb7f..525417f 100644 --- a/actionpack/lib/action_controller/rewindable_input.rb +++ b/actionpack/lib/action_controller/rewindable_input.rb @@ -1,27 +1,18 @@ module ActionController class RewindableInput - class RewindableIO < ActiveSupport::BasicObject - def initialize(io) - @io = io - @rewindable = io.is_a?(::StringIO) - end - - def method_missing(method, *args, &block) - unless @rewindable - @io = ::StringIO.new(@io.read) - @rewindable = true - end - - @io.__send__(method, *args, &block) - end - end - def initialize(app) @app = app end def call(env) - env['rack.input'] = RewindableIO.new(env['rack.input']) + begin + env['rack.input'].rewind + rescue NoMethodError, Errno::ESPIPE + # Handles exceptions raised by input streams that cannot be rewound + # such as when using plain CGI under Apache + env['rack.input'] = StringIO.new(env['rack.input'].read) + end + @app.call(env) end end diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index 7887b71..1e6243f 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -94,7 +94,7 @@ class DispatcherTest < Test::Unit::TestCase def dispatch(cache_classes = true) ActionController::Routing::RouteSet.any_instance.stubs(:call).returns([200, {}, 'response']) Dispatcher.define_dispatcher_callbacks(cache_classes) - Dispatcher.new.call({}) + Dispatcher.new.call({'rack.input' => StringIO.new('')}) end def assert_subclasses(howmany, klass, message = klass.subclasses.inspect) diff --git a/actionpack/test/controller/request/multipart_params_parsing_test.rb b/actionpack/test/controller/request/multipart_params_parsing_test.rb index 7099fc4..a7b7e53 100644 --- a/actionpack/test/controller/request/multipart_params_parsing_test.rb +++ b/actionpack/test/controller/request/multipart_params_parsing_test.rb @@ -207,6 +207,7 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest def call(env) env['rack.input'].read + env['rack.input'].rewind @app.call(env) end end diff --git a/actionpack/test/controller/request/url_encoded_params_parsing_test.rb b/actionpack/test/controller/request/url_encoded_params_parsing_test.rb index 9936b77..36fbbe5 100644 --- a/actionpack/test/controller/request/url_encoded_params_parsing_test.rb +++ b/actionpack/test/controller/request/url_encoded_params_parsing_test.rb @@ -151,6 +151,7 @@ class UrlEncodedParamsParsingTest < ActionController::IntegrationTest def call(env) env['rack.input'].read + env['rack.input'].rewind @app.call(env) end end -- 1.6.4.2 From 375e8976e34b11ee25e75c30959d7c3fc3be6866 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 20 Apr 2009 13:51:11 +0100 Subject: [PATCH 014/779] Ensure JoinAssociation uses aliased table name when multiple associations have hash conditions on the same table --- activerecord/lib/active_record/associations.rb | 2 +- .../associations/association_proxy.rb | 4 ++-- activerecord/lib/active_record/base.rb | 4 ++-- .../associations/inner_join_association_test.rb | 5 +++++ activerecord/test/models/author.rb | 2 ++ 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 53a7105..fa18822 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2123,7 +2123,7 @@ module ActiveRecord klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record? [through_reflection, reflection].each do |ref| - join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions] + join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))} " if ref && ref.options[:conditions] end join diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 676c4ac..241b9bf 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -169,8 +169,8 @@ module ActiveRecord end # Forwards the call to the reflection class. - def sanitize_sql(sql) - @reflection.klass.send(:sanitize_sql, sql) + def sanitize_sql(sql, table_name = @reflection.klass.quoted_table_name) + @reflection.klass.send(:sanitize_sql, sql, table_name) end # Assigns the ID of the owner to the corresponding foreign key in +record+. diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2a53851..84b22a5 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2228,12 +2228,12 @@ module ActiveRecord #:nodoc: # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'" # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'" # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'" - def sanitize_sql_for_conditions(condition) + def sanitize_sql_for_conditions(condition, table_name = quoted_table_name) return nil if condition.blank? case condition when Array; sanitize_sql_array(condition) - when Hash; sanitize_sql_hash_for_conditions(condition) + when Hash; sanitize_sql_hash_for_conditions(condition, table_name) else condition end end diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index f87c914..7141531 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -29,6 +29,11 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase assert_match /INNER JOIN .?categories.? ON.*AND.*.?General.?.*TERMINATING_MARKER/, sql end + def test_construct_finder_sql_applies_aliases_tables_on_association_conditions + result = Author.find(:all, :joins => [:thinking_posts, :welcome_posts]) + assert_equal authors(:david), result.first + end + def test_construct_finder_sql_unpacks_nested_joins sql = Author.send(:construct_finder_sql, :joins => {:posts => [[:comments]]}) assert_no_match /inner join.*inner join.*inner join/i, sql, "only two join clauses should be present" diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 4ffac4f..669c664 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -25,6 +25,8 @@ class Author < ActiveRecord::Base has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'" has_many :comments_with_include, :through => :posts, :source => :comments, :include => :post + has_many :thinking_posts, :class_name => 'Post', :conditions => { :title => 'So I was thinking' } + has_many :welcome_posts, :class_name => 'Post', :conditions => { :title => 'Welcome to the weblog' } has_many :comments_desc, :through => :posts, :source => :comments, :order => 'comments.id DESC' has_many :limited_comments, :through => :posts, :source => :comments, :limit => 1 -- 1.6.4.2 From fc2421b78402c430f9037c4db81779129e3c43c8 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 20 Apr 2009 18:12:40 +0100 Subject: [PATCH 015/779] Ensure :dependent => :delete_all works for association with hash conditions --- activerecord/lib/active_record/associations.rb | 2 +- .../associations/has_many_associations_test.rb | 6 ++++++ activerecord/test/models/author.rb | 2 +- activerecord/test/models/company.rb | 13 ++++++------- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index fa18822..2115878 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1375,7 +1375,7 @@ module ActiveRecord dependent_conditions = [] dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}" dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as] - dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions] + dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.quoted_table_name) if reflection.options[:conditions] dependent_conditions << extra_conditions if extra_conditions dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ") dependent_conditions = dependent_conditions.gsub('@', '\@') diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 30edf79..5df74fc 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -719,6 +719,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert Client.find(:all, :conditions => "firm_id=#{firm.id}").empty? end + def test_dependence_for_associations_with_hash_condition + david = authors(:david) + post = posts(:thinking).id + assert_difference('Post.count', -1) { assert david.destroy } + end + def test_destroy_dependent_when_deleted_from_association firm = Firm.find(:first) assert_equal 2, firm.clients.size diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 669c664..0d9ee36 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -25,7 +25,7 @@ class Author < ActiveRecord::Base has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'" has_many :comments_with_include, :through => :posts, :source => :comments, :include => :post - has_many :thinking_posts, :class_name => 'Post', :conditions => { :title => 'So I was thinking' } + has_many :thinking_posts, :class_name => 'Post', :conditions => { :title => 'So I was thinking' }, :dependent => :delete_all has_many :welcome_posts, :class_name => 'Post', :conditions => { :title => 'Welcome to the weblog' } has_many :comments_desc, :through => :posts, :source => :comments, :order => 'comments.id DESC' diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 02a775f..eb68153 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -78,13 +78,6 @@ class DependentFirm < Company has_many :companies, :foreign_key => 'client_of', :order => "id", :dependent => :nullify end -class ExclusivelyDependentFirm < Company - has_one :account, :foreign_key => "firm_id", :dependent => :delete - has_many :dependent_sanitized_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => "name = 'BigShot Inc.'" - has_many :dependent_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => ["name = ?", 'BigShot Inc.'] - has_many :dependent_hash_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => {:name => 'BigShot Inc.'} -end - class Client < Company belongs_to :firm, :foreign_key => "client_of" belongs_to :firm_with_basic_id, :class_name => "Firm", :foreign_key => "firm_id" @@ -125,6 +118,12 @@ class Client < Company end end +class ExclusivelyDependentFirm < Company + has_one :account, :foreign_key => "firm_id", :dependent => :delete + has_many :dependent_sanitized_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => "name = 'BigShot Inc.'" + has_many :dependent_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => ["name = ?", 'BigShot Inc.'] + has_many :dependent_hash_conditional_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all, :conditions => {:name => 'BigShot Inc.'} +end class SpecialClient < Client end -- 1.6.4.2 From 60601234708b34c81dcd6a58a0cad79a7520ce10 Mon Sep 17 00:00:00 2001 From: Max Lapshin Date: Sat, 4 Apr 2009 18:11:33 +0100 Subject: [PATCH 016/779] Support multiple schemas in table names for postgresql [#390 state:resolved] Signed-off-by: Pratik Naik --- .../connection_adapters/postgresql_adapter.rb | 31 +++++++++++++- activerecord/test/cases/schema_test_postgresql.rb | 44 ++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 913bb52..ec204d0 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -392,9 +392,28 @@ module ActiveRecord quote_string(s) end + # Checks the following cases: + # + # - table_name + # - "table.name" + # - schema_name.table_name + # - schema_name."table.name" + # - "schema.name".table_name + # - "schema.name"."table.name" + def quote_table_name(name) + schema, name_part = extract_pg_identifier_from_name(name.to_s) + + unless name_part + quote_column_name(schema) + else + table_name, name_part = extract_pg_identifier_from_name(name_part) + "#{quote_column_name(schema)}.#{quote_column_name(table_name)}" + end + end + # Quotes column names for use in SQL queries. def quote_column_name(name) #:nodoc: - %("#{name}") + PGconn.quote_ident(name.to_s) end # Quote date/time values for use in SQL input. Includes microseconds @@ -1045,6 +1064,16 @@ module ActiveRecord ORDER BY a.attnum end_sql end + + def extract_pg_identifier_from_name(name) + match_data = name[0,1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/) + + if match_data + rest = name[match_data[0].length..-1] + rest = rest[1..-1] if rest[0,1] == "." + [match_data[1], (rest.length > 0 ? rest : nil)] + end + end end end end diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/schema_test_postgresql.rb index 336a387..2d36bd0 100644 --- a/activerecord/test/cases/schema_test_postgresql.rb +++ b/activerecord/test/cases/schema_test_postgresql.rb @@ -18,9 +18,22 @@ class SchemaTest < ActiveRecord::TestCase 'moment timestamp without time zone default now()' ] + class Thing1 < ActiveRecord::Base + set_table_name "test_schema.things" + end + + class Thing2 < ActiveRecord::Base + set_table_name "test_schema2.things" + end + + class Thing3 < ActiveRecord::Base + set_table_name 'test_schema."things.table"' + end + def setup @connection = ActiveRecord::Base.connection @connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" + @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})" @connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" @@ -47,6 +60,37 @@ class SchemaTest < ActiveRecord::TestCase end end + + def test_proper_encoding_of_table_name + assert_equal '"table_name"', @connection.quote_table_name('table_name') + assert_equal '"table.name"', @connection.quote_table_name('"table.name"') + assert_equal '"schema_name"."table_name"', @connection.quote_table_name('schema_name.table_name') + assert_equal '"schema_name"."table.name"', @connection.quote_table_name('schema_name."table.name"') + assert_equal '"schema.name"."table_name"', @connection.quote_table_name('"schema.name".table_name') + assert_equal '"schema.name"."table.name"', @connection.quote_table_name('"schema.name"."table.name"') + end + + def test_classes_with_qualified_schema_name + assert_equal 0, Thing1.count + assert_equal 0, Thing2.count + assert_equal 0, Thing3.count + + Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 0, Thing2.count + assert_equal 0, Thing3.count + + Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 1, Thing2.count + assert_equal 0, Thing3.count + + Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 1, Thing2.count + assert_equal 1, Thing3.count + end + def test_raise_on_unquoted_schema_name assert_raise(ActiveRecord::StatementInvalid) do with_schema_search_path '$user,public' -- 1.6.4.2 From de4cc53f7428090600b9e31ee39cf8c4c0df811e Mon Sep 17 00:00:00 2001 From: Max Lapshin Date: Mon, 20 Apr 2009 20:08:51 +0400 Subject: [PATCH 017/779] Fixed wrong quoting of index names in postgres [#2402 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tarmo Tänav --- .../connection_adapters/postgresql_adapter.rb | 2 +- activerecord/test/cases/schema_test_postgresql.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index ec204d0..15e155d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -858,7 +858,7 @@ module ActiveRecord # Drops an index from a table. def remove_index(table_name, options = {}) - execute "DROP INDEX #{index_name(table_name, options)}" + execute "DROP INDEX #{quote_table_name(index_name(table_name, options))}" end # Maps logical Rails types to PostgreSQL-specific data types. diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/schema_test_postgresql.rb index 2d36bd0..ed8e1d2 100644 --- a/activerecord/test/cases/schema_test_postgresql.rb +++ b/activerecord/test/cases/schema_test_postgresql.rb @@ -113,6 +113,16 @@ class SchemaTest < ActiveRecord::TestCase do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2) end + def test_with_uppercase_index_name + ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" + assert_nothing_raised { ActiveRecord::Base.connection.remove_index :things, :name => "#{SCHEMA_NAME}.things_Index"} + + ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" + ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME + assert_nothing_raised { ActiveRecord::Base.connection.remove_index :things, :name => "things_Index"} + ActiveRecord::Base.connection.schema_search_path = "public" + end + private def columns(table_name) @connection.send(:column_definitions, table_name).map do |name, type, default| -- 1.6.4.2 From 70ba90b072025b89248606178ee30d2ff12301c4 Mon Sep 17 00:00:00 2001 From: Scott Woods Date: Mon, 20 Apr 2009 23:48:02 +0300 Subject: [PATCH 018/779] Quote table names when casting to regclass so that capitalized tables are supported. [#2418 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tarmo Tänav --- .../connection_adapters/postgresql_adapter.rb | 6 ++-- activerecord/test/cases/schema_dumper_test.rb | 5 ++++ activerecord/test/cases/schema_test_postgresql.rb | 22 ++++++++++++++++++++ activerecord/test/schema/schema.rb | 4 +++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 15e155d..74ee6a1 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -764,7 +764,7 @@ module ActiveRecord AND attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1] AND cons.contype = 'p' - AND dep.refobjid = '#{table}'::regclass + AND dep.refobjid = '#{quote_table_name(table)}'::regclass end_sql if result.nil? or result.empty? @@ -783,7 +783,7 @@ module ActiveRecord JOIN pg_attribute attr ON (t.oid = attrelid) JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum) JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1]) - WHERE t.oid = '#{table}'::regclass + WHERE t.oid = '#{quote_table_name(table)}'::regclass AND cons.contype = 'p' AND def.adsrc ~* 'nextval' end_sql @@ -1059,7 +1059,7 @@ module ActiveRecord SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum - WHERE a.attrelid = '#{table_name}'::regclass + WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum end_sql diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 17e4c75..0b1a075 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -22,6 +22,11 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_no_match %r{create_table "sqlite_sequence"}, output end + def test_schema_dump_includes_camelcase_table_name + output = standard_dump + assert_match %r{create_table "CamelCase"}, output + end + def assert_line_up(lines, pattern, required = false) return assert(true) if lines.empty? matches = lines.map { |line| line.match(pattern) } diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/schema_test_postgresql.rb index ed8e1d2..a294848 100644 --- a/activerecord/test/cases/schema_test_postgresql.rb +++ b/activerecord/test/cases/schema_test_postgresql.rb @@ -6,6 +6,7 @@ class SchemaTest < ActiveRecord::TestCase SCHEMA_NAME = 'test_schema' SCHEMA2_NAME = 'test_schema2' TABLE_NAME = 'things' + CAPITALIZED_TABLE_NAME = 'Things' INDEX_A_NAME = 'a_index_things_on_name' INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema' INDEX_A_COLUMN = 'name' @@ -30,10 +31,15 @@ class SchemaTest < ActiveRecord::TestCase set_table_name 'test_schema."things.table"' end + class Thing4 < ActiveRecord::Base + set_table_name 'test_schema."Things"' + end + def setup @connection = ActiveRecord::Base.connection @connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})" + @connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{CAPITALIZED_TABLE_NAME}\" (#{COLUMNS.join(',')})" @connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})" @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" @connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});" @@ -52,6 +58,12 @@ class SchemaTest < ActiveRecord::TestCase end end + def test_with_schema_prefixed_capitalized_table_name + assert_nothing_raised do + assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{CAPITALIZED_TABLE_NAME}") + end + end + def test_with_schema_search_path assert_nothing_raised do with_schema_search_path(SCHEMA_NAME) do @@ -74,21 +86,31 @@ class SchemaTest < ActiveRecord::TestCase assert_equal 0, Thing1.count assert_equal 0, Thing2.count assert_equal 0, Thing3.count + assert_equal 0, Thing4.count Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) assert_equal 1, Thing1.count assert_equal 0, Thing2.count assert_equal 0, Thing3.count + assert_equal 0, Thing4.count Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) assert_equal 1, Thing1.count assert_equal 1, Thing2.count assert_equal 0, Thing3.count + assert_equal 0, Thing4.count Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) assert_equal 1, Thing1.count assert_equal 1, Thing2.count assert_equal 1, Thing3.count + assert_equal 0, Thing4.count + + Thing4.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now) + assert_equal 1, Thing1.count + assert_equal 1, Thing2.count + assert_equal 1, Thing3.count + assert_equal 1, Thing4.count end def test_raise_on_unquoted_schema_name diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 5640510..3b073b0 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -68,6 +68,10 @@ ActiveRecord::Schema.define do t.boolean :value end + create_table "CamelCase", :force => true do |t| + t.string :name + end + create_table :categories, :force => true do |t| t.string :name, :null => false t.string :type -- 1.6.4.2 From 5a4603fafbf4d959ebc8f9435457eaf6fa4004e9 Mon Sep 17 00:00:00 2001 From: Max Lapshin Date: Mon, 20 Apr 2009 19:47:31 +0400 Subject: [PATCH 019/779] Fixed dumping from postgresql columns in index in wrong order. [#2515 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tarmo Tänav --- .../connection_adapters/postgresql_adapter.rb | 33 +++++++++++--------- activerecord/test/cases/schema_dumper_test.rb | 5 +++ activerecord/test/schema/schema.rb | 2 + 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 74ee6a1..4961793 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -640,33 +640,36 @@ module ActiveRecord def indexes(table_name, name = nil) schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',') result = query(<<-SQL, name) - SELECT distinct i.relname, d.indisunique, a.attname - FROM pg_class t, pg_class i, pg_index d, pg_attribute a + SELECT distinct i.relname, d.indisunique, d.indkey, t.oid + FROM pg_class t, pg_class i, pg_index d WHERE i.relkind = 'i' AND d.indexrelid = i.oid AND d.indisprimary = 'f' AND t.oid = d.indrelid AND t.relname = '#{table_name}' AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) ) - AND a.attrelid = t.oid - AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum - OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum - OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum - OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum - OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum ) ORDER BY i.relname SQL - current_index = nil + indexes = [] - result.each do |row| - if current_index != row[0] - indexes << IndexDefinition.new(table_name, row[0], row[1] == "t", []) - current_index = row[0] - end + indexes = result.map do |row| + index_name = row[0] + unique = row[1] == 't' + indkey = row[2].split(" ") + oid = row[3] + + columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = r[0]; attlist} + SELECT a.attname, a.attnum + FROM pg_attribute a + WHERE a.attrelid = #{oid} + AND a.attnum IN (#{indkey.join(",")}) + SQL + + column_names = indkey.map {|attnum| columns[attnum] } + IndexDefinition.new(table_name, index_name, unique, column_names) - indexes.last.columns << row[2] end indexes diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 0b1a075..f9ad7f3 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -152,6 +152,11 @@ class SchemaDumperTest < ActiveRecord::TestCase end end + def test_schema_dumps_index_columns_in_right_order + index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip + assert_equal 'add_index "companies", ["firm_id", "type", "rating", "ruby_type"], :name => "company_index"', index_definition + end + if current_adapter?(:MysqlAdapter) def test_schema_dump_should_not_add_default_value_for_mysql_text_field output = standard_dump diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 3b073b0..98e6d19 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -118,6 +118,8 @@ ActiveRecord::Schema.define do t.integer :rating, :default => 1 end + add_index :companies, [:firm_id, :type, :rating, :ruby_type], :name => "company_index" + create_table :computers, :force => true do |t| t.integer :developer, :null => false t.integer :extendedWarranty, :null => false -- 1.6.4.2 From 326709739361e1087131da0fa9b6cbd4bce5be8e Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 21 Apr 2009 13:06:26 +0100 Subject: [PATCH 020/779] Fix tests for sqlite3 3.6.xx --- activerecord/test/models/project.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb index 550d4ae..f25b2dd 100644 --- a/activerecord/test/models/project.rb +++ b/activerecord/test/models/project.rb @@ -13,7 +13,7 @@ class Project < ActiveRecord::Base :after_add => Proc.new {|o, r| o.developers_log << "after_adding#{r.id || ''}"}, :before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"}, :after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"} - has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" + has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "developers.salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" attr_accessor :developers_log -- 1.6.4.2 From 5bbd097ce990d6830c832435867b06108de5bf08 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 21 Apr 2009 13:11:56 +0100 Subject: [PATCH 021/779] Specify :group with the table name for it to work on sqlite3 --- activerecord/test/cases/base_test.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 99d7796..43cda45 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1756,7 +1756,7 @@ class BasicsTest < ActiveRecord::TestCase end def test_scoped_find_with_group_and_having - developers = Developer.with_scope(:find => { :group => 'salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do + developers = Developer.with_scope(:find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do Developer.find(:all) end assert_equal 3, developers.size -- 1.6.4.2 From 09a976ac58d2d7637003b92d51637f59f647b53a Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 21 Apr 2009 13:28:49 +0100 Subject: [PATCH 022/779] Change table to prevent copying indexes on sqlite2 --- activerecord/test/cases/copy_table_test_sqlite.rb | 10 +++++----- 1 files changed, 5 insertions(+), 5 deletions(-) diff --git a/activerecord/test/cases/copy_table_test_sqlite.rb b/activerecord/test/cases/copy_table_test_sqlite.rb index 72bd7e2..de8af30 100644 --- a/activerecord/test/cases/copy_table_test_sqlite.rb +++ b/activerecord/test/cases/copy_table_test_sqlite.rb @@ -10,7 +10,7 @@ class CopyTableTest < ActiveRecord::TestCase end end - def test_copy_table(from = 'companies', to = 'companies2', options = {}) + def test_copy_table(from = 'customers', to = 'customers2', options = {}) assert_nothing_raised {copy_table(from, to, options)} assert_equal row_count(from), row_count(to) @@ -24,11 +24,11 @@ class CopyTableTest < ActiveRecord::TestCase end def test_copy_table_renaming_column - test_copy_table('companies', 'companies2', - :rename => {'client_of' => 'fan_of'}) do |from, to, options| - expected = column_values(from, 'client_of') + test_copy_table('customers', 'customers2', + :rename => {'name' => 'person_name'}) do |from, to, options| + expected = column_values(from, 'name') assert expected.any?, 'only nils in resultset; real values are needed' - assert_equal expected, column_values(to, 'fan_of') + assert_equal expected, column_values(to, 'person_name') end end -- 1.6.4.2 From 2d9b45722c5639ad822f9a213edfd4d2bb084dcb Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 25 Apr 2009 13:44:34 -0500 Subject: [PATCH 023/779] Remove pending rack specifications until they are official --- actionpack/lib/action_controller/failsafe.rb | 2 +- actionpack/lib/action_controller/integration.rb | 4 +--- actionpack/lib/action_controller/request.rb | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_controller/failsafe.rb b/actionpack/lib/action_controller/failsafe.rb index 5675811..644eb72 100644 --- a/actionpack/lib/action_controller/failsafe.rb +++ b/actionpack/lib/action_controller/failsafe.rb @@ -11,7 +11,7 @@ module ActionController @app.call(env) rescue Exception => exception # Reraise exception in test environment - if env["rack.test"] + if defined?(Rails) && Rails.test? raise exception else failsafe_response(exception) diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 26b6955..5f220a4 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -292,9 +292,7 @@ module ActionController "rack.errors" => StringIO.new, "rack.multithread" => true, "rack.multiprocess" => true, - "rack.run_once" => false, - - "rack.test" => true + "rack.run_once" => false ) (headers || {}).each do |key, value| diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index ef223f1..306b95e 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -383,7 +383,7 @@ EOM alias_method :params, :parameters def path_parameters=(parameters) #:nodoc: - @env["rack.routing_args"] = parameters + @env["action_controller.request.path_parameters"] = parameters @symbolized_path_parameters = @parameters = nil end @@ -399,7 +399,7 @@ EOM # # See symbolized_path_parameters for symbolized keys. def path_parameters - @env["rack.routing_args"] ||= {} + @env["action_controller.request.path_parameters"] ||= {} end # The request body is an IO input stream. If the RAW_POST_DATA environment -- 1.6.4.2 From 16f36b6171c855dcc92ef557c454fcfc314b617f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 25 Apr 2009 13:59:26 -0500 Subject: [PATCH 024/779] Remove vendored version of Rack --- actionpack/lib/action_controller.rb | 8 +- actionpack/lib/action_controller/request.rb | 4 + .../lib/action_controller/vendor/rack-1.0/rack.rb | 89 ---- .../vendor/rack-1.0/rack/adapter/camping.rb | 22 - .../vendor/rack-1.0/rack/auth/abstract/handler.rb | 37 -- .../vendor/rack-1.0/rack/auth/abstract/request.rb | 37 -- .../vendor/rack-1.0/rack/auth/basic.rb | 58 --- .../vendor/rack-1.0/rack/auth/digest/md5.rb | 124 ----- .../vendor/rack-1.0/rack/auth/digest/nonce.rb | 51 -- .../vendor/rack-1.0/rack/auth/digest/params.rb | 55 --- .../vendor/rack-1.0/rack/auth/digest/request.rb | 40 -- .../vendor/rack-1.0/rack/auth/openid.rb | 480 -------------------- .../vendor/rack-1.0/rack/builder.rb | 63 --- .../vendor/rack-1.0/rack/cascade.rb | 36 -- .../vendor/rack-1.0/rack/chunked.rb | 49 -- .../vendor/rack-1.0/rack/commonlogger.rb | 61 --- .../vendor/rack-1.0/rack/conditionalget.rb | 45 -- .../vendor/rack-1.0/rack/content_length.rb | 29 -- .../vendor/rack-1.0/rack/content_type.rb | 23 - .../vendor/rack-1.0/rack/deflater.rb | 85 ---- .../vendor/rack-1.0/rack/directory.rb | 153 ------- .../action_controller/vendor/rack-1.0/rack/file.rb | 88 ---- .../vendor/rack-1.0/rack/handler.rb | 48 -- .../vendor/rack-1.0/rack/handler/cgi.rb | 61 --- .../rack-1.0/rack/handler/evented_mongrel.rb | 8 - .../vendor/rack-1.0/rack/handler/fastcgi.rb | 89 ---- .../vendor/rack-1.0/rack/handler/lsws.rb | 55 --- .../vendor/rack-1.0/rack/handler/mongrel.rb | 84 ---- .../vendor/rack-1.0/rack/handler/scgi.rb | 59 --- .../rack-1.0/rack/handler/swiftiplied_mongrel.rb | 8 - .../vendor/rack-1.0/rack/handler/thin.rb | 18 - .../vendor/rack-1.0/rack/handler/webrick.rb | 67 --- .../action_controller/vendor/rack-1.0/rack/head.rb | 19 - .../action_controller/vendor/rack-1.0/rack/lint.rb | 462 ------------------- .../vendor/rack-1.0/rack/lobster.rb | 65 --- .../action_controller/vendor/rack-1.0/rack/lock.rb | 16 - .../vendor/rack-1.0/rack/methodoverride.rb | 27 -- .../action_controller/vendor/rack-1.0/rack/mime.rb | 204 --------- .../action_controller/vendor/rack-1.0/rack/mock.rb | 160 ------- .../vendor/rack-1.0/rack/recursive.rb | 57 --- .../vendor/rack-1.0/rack/reloader.rb | 64 --- .../vendor/rack-1.0/rack/request.rb | 241 ---------- .../vendor/rack-1.0/rack/response.rb | 179 -------- .../vendor/rack-1.0/rack/session/abstract/id.rb | 142 ------ .../vendor/rack-1.0/rack/session/cookie.rb | 91 ---- .../vendor/rack-1.0/rack/session/memcache.rb | 109 ----- .../vendor/rack-1.0/rack/session/pool.rb | 100 ---- .../vendor/rack-1.0/rack/showexceptions.rb | 349 -------------- .../vendor/rack-1.0/rack/showstatus.rb | 106 ----- .../vendor/rack-1.0/rack/static.rb | 38 -- .../vendor/rack-1.0/rack/urlmap.rb | 55 --- .../vendor/rack-1.0/rack/utils.rb | 392 ---------------- .../request/multipart_params_parsing_test.rb | 2 +- 53 files changed, 7 insertions(+), 5005 deletions(-) delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/adapter/camping.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/request.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/basic.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/nonce.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/params.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/openid.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/cascade.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/commonlogger.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/conditionalget.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/handler.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/evented_mongrel.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/swiftiplied_mongrel.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/head.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/lobster.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/lock.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/methodoverride.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/mime.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/mock.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/recursive.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/reloader.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/request.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/session/abstract/id.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/session/cookie.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/session/memcache.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/session/pool.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/showexceptions.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/static.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index d03f4cb..8f8820e 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -31,12 +31,8 @@ rescue LoadError end end -begin - gem 'rack', '~> 1.0.0' - require 'rack' -rescue Gem::LoadError - require 'action_controller/vendor/rack-1.0/rack' -end +gem 'rack', '~> 1.0.0' +require 'rack' module ActionController # TODO: Review explicit to see if they will automatically be handled by diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 306b95e..1c3c1c8 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -95,6 +95,10 @@ module ActionController end end + def media_type + content_type.to_s + end + # Returns the accepted MIME type for the request. def accepts @accepts ||= begin diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb deleted file mode 100644 index 6349b95..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) 2007, 2008, 2009 Christian Neukirchen -# -# Rack is freely distributable under the terms of an MIT-style license. -# See COPYING or http://www.opensource.org/licenses/mit-license.php. - -$:.unshift(File.expand_path(File.dirname(__FILE__))) - - -# The Rack main module, serving as a namespace for all core Rack -# modules and classes. -# -# All modules meant for use in your application are autoloaded here, -# so it should be enough just to require rack.rb in your code. - -module Rack - # The Rack protocol version number implemented. - VERSION = [0,1] - - # Return the Rack protocol version as a dotted string. - def self.version - VERSION.join(".") - end - - # Return the Rack release as a dotted string. - def self.release - "1.0 bundled" - end - - autoload :Builder, "rack/builder" - autoload :Cascade, "rack/cascade" - autoload :Chunked, "rack/chunked" - autoload :CommonLogger, "rack/commonlogger" - autoload :ConditionalGet, "rack/conditionalget" - autoload :ContentLength, "rack/content_length" - autoload :ContentType, "rack/content_type" - autoload :File, "rack/file" - autoload :Deflater, "rack/deflater" - autoload :Directory, "rack/directory" - autoload :ForwardRequest, "rack/recursive" - autoload :Handler, "rack/handler" - autoload :Head, "rack/head" - autoload :Lint, "rack/lint" - autoload :Lock, "rack/lock" - autoload :MethodOverride, "rack/methodoverride" - autoload :Mime, "rack/mime" - autoload :Recursive, "rack/recursive" - autoload :Reloader, "rack/reloader" - autoload :ShowExceptions, "rack/showexceptions" - autoload :ShowStatus, "rack/showstatus" - autoload :Static, "rack/static" - autoload :URLMap, "rack/urlmap" - autoload :Utils, "rack/utils" - - autoload :MockRequest, "rack/mock" - autoload :MockResponse, "rack/mock" - - autoload :Request, "rack/request" - autoload :Response, "rack/response" - - module Auth - autoload :Basic, "rack/auth/basic" - autoload :AbstractRequest, "rack/auth/abstract/request" - autoload :AbstractHandler, "rack/auth/abstract/handler" - autoload :OpenID, "rack/auth/openid" - module Digest - autoload :MD5, "rack/auth/digest/md5" - autoload :Nonce, "rack/auth/digest/nonce" - autoload :Params, "rack/auth/digest/params" - autoload :Request, "rack/auth/digest/request" - end - end - - module Session - autoload :Cookie, "rack/session/cookie" - autoload :Pool, "rack/session/pool" - autoload :Memcache, "rack/session/memcache" - end - - # *Adapters* connect Rack with third party web frameworks. - # - # Rack includes an adapter for Camping, see README for other - # frameworks supporting Rack in their code bases. - # - # Refer to the submodules for framework-specific calling details. - - module Adapter - autoload :Camping, "rack/adapter/camping" - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/adapter/camping.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/adapter/camping.rb deleted file mode 100644 index 63bc787..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/adapter/camping.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Rack - module Adapter - class Camping - def initialize(app) - @app = app - end - - def call(env) - env["PATH_INFO"] ||= "" - env["SCRIPT_NAME"] ||= "" - controller = @app.run(env['rack.input'], env) - h = controller.headers - h.each_pair do |k,v| - if v.kind_of? URI - h[k] = v.to_s - end - end - [controller.status, controller.headers, [controller.body.to_s]] - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb deleted file mode 100644 index 214df62..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Rack - module Auth - # Rack::Auth::AbstractHandler implements common authentication functionality. - # - # +realm+ should be set for all handlers. - - class AbstractHandler - - attr_accessor :realm - - def initialize(app, realm=nil, &authenticator) - @app, @realm, @authenticator = app, realm, authenticator - end - - - private - - def unauthorized(www_authenticate = challenge) - return [ 401, - { 'Content-Type' => 'text/plain', - 'Content-Length' => '0', - 'WWW-Authenticate' => www_authenticate.to_s }, - [] - ] - end - - def bad_request - return [ 400, - { 'Content-Type' => 'text/plain', - 'Content-Length' => '0' }, - [] - ] - end - - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/request.rb deleted file mode 100644 index 1d9ccec..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/request.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Rack - module Auth - class AbstractRequest - - def initialize(env) - @env = env - end - - def provided? - !authorization_key.nil? - end - - def parts - @parts ||= @env[authorization_key].split(' ', 2) - end - - def scheme - @scheme ||= parts.first.downcase.to_sym - end - - def params - @params ||= parts.last - end - - - private - - AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION'] - - def authorization_key - @authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) } - end - - end - - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/basic.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/basic.rb deleted file mode 100644 index 9557224..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/basic.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'rack/auth/abstract/handler' -require 'rack/auth/abstract/request' - -module Rack - module Auth - # Rack::Auth::Basic implements HTTP Basic Authentication, as per RFC 2617. - # - # Initialize with the Rack application that you want protecting, - # and a block that checks if a username and password pair are valid. - # - # See also: example/protectedlobster.rb - - class Basic < AbstractHandler - - def call(env) - auth = Basic::Request.new(env) - - return unauthorized unless auth.provided? - - return bad_request unless auth.basic? - - if valid?(auth) - env['REMOTE_USER'] = auth.username - - return @app.call(env) - end - - unauthorized - end - - - private - - def challenge - 'Basic realm="%s"' % realm - end - - def valid?(auth) - @authenticator.call(*auth.credentials) - end - - class Request < Auth::AbstractRequest - def basic? - :basic == scheme - end - - def credentials - @credentials ||= params.unpack("m*").first.split(/:/, 2) - end - - def username - credentials.first - end - end - - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb deleted file mode 100644 index e579dc9..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb +++ /dev/null @@ -1,124 +0,0 @@ -require 'rack/auth/abstract/handler' -require 'rack/auth/digest/request' -require 'rack/auth/digest/params' -require 'rack/auth/digest/nonce' -require 'digest/md5' - -module Rack - module Auth - module Digest - # Rack::Auth::Digest::MD5 implements the MD5 algorithm version of - # HTTP Digest Authentication, as per RFC 2617. - # - # Initialize with the [Rack] application that you want protecting, - # and a block that looks up a plaintext password for a given username. - # - # +opaque+ needs to be set to a constant base64/hexadecimal string. - # - class MD5 < AbstractHandler - - attr_accessor :opaque - - attr_writer :passwords_hashed - - def initialize(*args) - super - @passwords_hashed = nil - end - - def passwords_hashed? - !!@passwords_hashed - end - - def call(env) - auth = Request.new(env) - - unless auth.provided? - return unauthorized - end - - if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth) - return bad_request - end - - if valid?(auth) - if auth.nonce.stale? - return unauthorized(challenge(:stale => true)) - else - env['REMOTE_USER'] = auth.username - - return @app.call(env) - end - end - - unauthorized - end - - - private - - QOP = 'auth'.freeze - - def params(hash = {}) - Params.new do |params| - params['realm'] = realm - params['nonce'] = Nonce.new.to_s - params['opaque'] = H(opaque) - params['qop'] = QOP - - hash.each { |k, v| params[k] = v } - end - end - - def challenge(hash = {}) - "Digest #{params(hash)}" - end - - def valid?(auth) - valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth) - end - - def valid_qop?(auth) - QOP == auth.qop - end - - def valid_opaque?(auth) - H(opaque) == auth.opaque - end - - def valid_nonce?(auth) - auth.nonce.valid? - end - - def valid_digest?(auth) - digest(auth, @authenticator.call(auth.username)) == auth.response - end - - def md5(data) - ::Digest::MD5.hexdigest(data) - end - - alias :H :md5 - - def KD(secret, data) - H([secret, data] * ':') - end - - def A1(auth, password) - [ auth.username, auth.realm, password ] * ':' - end - - def A2(auth) - [ auth.method, auth.uri ] * ':' - end - - def digest(auth, password) - password_hash = passwords_hashed? ? password : H(A1(auth, password)) - - KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, QOP, H(A2(auth)) ] * ':') - end - - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/nonce.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/nonce.rb deleted file mode 100644 index dbe109f..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/nonce.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'digest/md5' - -module Rack - module Auth - module Digest - # Rack::Auth::Digest::Nonce is the default nonce generator for the - # Rack::Auth::Digest::MD5 authentication handler. - # - # +private_key+ needs to set to a constant string. - # - # +time_limit+ can be optionally set to an integer (number of seconds), - # to limit the validity of the generated nonces. - - class Nonce - - class << self - attr_accessor :private_key, :time_limit - end - - def self.parse(string) - new(*string.unpack("m*").first.split(' ', 2)) - end - - def initialize(timestamp = Time.now, given_digest = nil) - @timestamp, @given_digest = timestamp.to_i, given_digest - end - - def to_s - [([ @timestamp, digest ] * ' ')].pack("m*").strip - end - - def digest - ::Digest::MD5.hexdigest([ @timestamp, self.class.private_key ] * ':') - end - - def valid? - digest == @given_digest - end - - def stale? - !self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit - end - - def fresh? - !stale? - end - - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/params.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/params.rb deleted file mode 100644 index 730e2ef..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/params.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Rack - module Auth - module Digest - class Params < Hash - - def self.parse(str) - split_header_value(str).inject(new) do |header, param| - k, v = param.split('=', 2) - header[k] = dequote(v) - header - end - end - - def self.dequote(str) # From WEBrick::HTTPUtils - ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup - ret.gsub!(/\\(.)/, "\\1") - ret - end - - def self.split_header_value(str) - str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] } - end - - def initialize - super - - yield self if block_given? - end - - def [](k) - super k.to_s - end - - def []=(k, v) - super k.to_s, v.to_s - end - - UNQUOTED = ['qop', 'nc', 'stale'] - - def to_s - inject([]) do |parts, (k, v)| - parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v)) - parts - end.join(', ') - end - - def quote(str) # From WEBrick::HTTPUtils - '"' << str.gsub(/[\\\"]/o, "\\\1") << '"' - end - - end - end - end -end - diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb deleted file mode 100644 index a8aa3bf..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'rack/auth/abstract/request' -require 'rack/auth/digest/params' -require 'rack/auth/digest/nonce' - -module Rack - module Auth - module Digest - class Request < Auth::AbstractRequest - - def method - @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD'] - end - - def digest? - :digest == scheme - end - - def correct_uri? - (@env['SCRIPT_NAME'].to_s + @env['PATH_INFO'].to_s) == uri - end - - def nonce - @nonce ||= Nonce.parse(params['nonce']) - end - - def params - @params ||= Params.parse(parts.last) - end - - def method_missing(sym) - if params.has_key? key = sym.to_s - return params[key] - end - super - end - - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/openid.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/openid.rb deleted file mode 100644 index c5f6a51..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/openid.rb +++ /dev/null @@ -1,480 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net - -gem 'ruby-openid', '~> 2' if defined? Gem -require 'rack/request' -require 'rack/utils' -require 'rack/auth/abstract/handler' -require 'uri' -require 'openid' #gem -require 'openid/extension' #gem -require 'openid/store/memory' #gem - -module Rack - class Request - def openid_request - @env['rack.auth.openid.request'] - end - - def openid_response - @env['rack.auth.openid.response'] - end - end - - module Auth - - # Rack::Auth::OpenID provides a simple method for setting up an OpenID - # Consumer. It requires the ruby-openid library from janrain to operate, - # as well as a rack method of session management. - # - # The ruby-openid home page is at http://openidenabled.com/ruby-openid/. - # - # The OpenID specifications can be found at - # http://openid.net/specs/openid-authentication-1_1.html - # and - # http://openid.net/specs/openid-authentication-2_0.html. Documentation - # for published OpenID extensions and related topics can be found at - # http://openid.net/developers/specs/. - # - # It is recommended to read through the OpenID spec, as well as - # ruby-openid's documentation, to understand what exactly goes on. However - # a setup as simple as the presented examples is enough to provide - # Consumer functionality. - # - # This library strongly intends to utilize the OpenID 2.0 features of the - # ruby-openid library, which provides OpenID 1.0 compatiblity. - # - # NOTE: Due to the amount of data that this library stores in the - # session, Rack::Session::Cookie may fault. - - class OpenID - - class NoSession < RuntimeError; end - class BadExtension < RuntimeError; end - # Required for ruby-openid - ValidStatus = [:success, :setup_needed, :cancel, :failure] - - # = Arguments - # - # The first argument is the realm, identifying the site they are trusting - # with their identity. This is required, also treated as the trust_root - # in OpenID 1.x exchanges. - # - # The optional second argument is a hash of options. - # - # == Options - # - # :return_to defines the url to return to after the client - # authenticates with the openid service provider. This url should point - # to where Rack::Auth::OpenID is mounted. If :return_to is not - # provided, return_to will be the current url which allows flexibility - # with caveats. - # - # :session_key defines the key to the session hash in the env. - # It defaults to 'rack.session'. - # - # :openid_param defines at what key in the request parameters to - # find the identifier to resolve. As per the 2.0 spec, the default is - # 'openid_identifier'. - # - # :store defined what OpenID Store to use for persistant - # information. By default a Store::Memory will be used. - # - # :immediate as true will make initial requests to be of an - # immediate type. This is false by default. See OpenID specification - # documentation. - # - # :extensions should be a hash of openid extension - # implementations. The key should be the extension main module, the value - # should be an array of arguments for extension::Request.new. - # The hash is iterated over and passed to #add_extension for processing. - # Please see #add_extension for further documentation. - # - # == Examples - # - # simple_oid = OpenID.new('http://mysite.com/') - # - # return_oid = OpenID.new('http://mysite.com/', { - # :return_to => 'http://mysite.com/openid' - # }) - # - # complex_oid = OpenID.new('http://mysite.com/', - # :immediate => true, - # :extensions => { - # ::OpenID::SReg => [['email'],['nickname']] - # } - # ) - # - # = Advanced - # - # Most of the functionality of this library is encapsulated such that - # expansion and overriding functions isn't difficult nor tricky. - # Alternately, to avoid opening up singleton objects or subclassing, a - # wrapper rack middleware can be composed to act upon Auth::OpenID's - # responses. See #check and #finish for locations of pertinent data. - # - # == Responses - # - # To change the responses that Auth::OpenID returns, override the methods - # #redirect, #bad_request, #unauthorized, #access_denied, and - # #foreign_server_failure. - # - # Additionally #confirm_post_params is used when the URI would exceed - # length limits on a GET request when doing the initial verification - # request. - # - # == Processing - # - # To change methods of processing completed transactions, override the - # methods #success, #setup_needed, #cancel, and #failure. Please ensure - # the returned object is a rack compatible response. - # - # The first argument is an OpenID::Response, the second is a - # Rack::Request of the current request, the last is the hash used in - # ruby-openid handling, which can be found manually at - # env['rack.session'][:openid]. - # - # This is useful if you wanted to expand the processing done, such as - # setting up user accounts. - # - # oid_app = Rack::Auth::OpenID.new realm, :return_to => return_to - # def oid_app.success oid, request, session - # user = Models::User[oid.identity_url] - # user ||= Models::User.create_from_openid oid - # request['rack.session'][:user] = user.id - # redirect MyApp.site_home - # end - # - # site_map['/openid'] = oid_app - # map = Rack::URLMap.new site_map - # ... - - def initialize(realm, options={}) - realm = URI(realm) - raise ArgumentError, "Invalid realm: #{realm}" \ - unless realm.absolute? \ - and realm.fragment.nil? \ - and realm.scheme =~ /^https?$/ \ - and realm.host =~ /^(\*\.)?#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+/ - realm.path = '/' if realm.path.empty? - @realm = realm.to_s - - if ruri = options[:return_to] - ruri = URI(ruri) - raise ArgumentError, "Invalid return_to: #{ruri}" \ - unless ruri.absolute? \ - and ruri.scheme =~ /^https?$/ \ - and ruri.fragment.nil? - raise ArgumentError, "return_to #{ruri} not within realm #{realm}" \ - unless self.within_realm?(ruri) - @return_to = ruri.to_s - end - - @session_key = options[:session_key] || 'rack.session' - @openid_param = options[:openid_param] || 'openid_identifier' - @store = options[:store] || ::OpenID::Store::Memory.new - @immediate = !!options[:immediate] - - @extensions = {} - if extensions = options.delete(:extensions) - extensions.each do |ext, args| - add_extension ext, *args - end - end - - # Undocumented, semi-experimental - @anonymous = !!options[:anonymous] - end - - attr_reader :realm, :return_to, :session_key, :openid_param, :store, - :immediate, :extensions - - # Sets up and uses session data at :openid within the session. - # Errors in this setup will raise a NoSession exception. - # - # If the parameter 'openid.mode' is set, which implies a followup from - # the openid server, processing is passed to #finish and the result is - # returned. However, if there is no appropriate openid information in the - # session, a 400 error is returned. - # - # If the parameter specified by options[:openid_param] is - # present, processing is passed to #check and the result is returned. - # - # If neither of these conditions are met, #unauthorized is called. - - def call(env) - env['rack.auth.openid'] = self - env_session = env[@session_key] - unless env_session and env_session.is_a?(Hash) - raise NoSession, 'No compatible session' - end - # let us work in our own namespace... - session = (env_session[:openid] ||= {}) - unless session and session.is_a?(Hash) - raise NoSession, 'Incompatible openid session' - end - - request = Rack::Request.new(env) - consumer = ::OpenID::Consumer.new(session, @store) - - if mode = request.GET['openid.mode'] - if session.key?(:openid_param) - finish(consumer, session, request) - else - bad_request - end - elsif request.GET[@openid_param] - check(consumer, session, request) - else - unauthorized - end - end - - # As the first part of OpenID consumer action, #check retrieves the data - # required for completion. - # - # If all parameters fit within the max length of a URI, a 303 redirect - # will be returned. Otherwise #confirm_post_params will be called. - # - # Any messages from OpenID's request are logged to env['rack.errors'] - # - # env['rack.auth.openid.request'] is the openid checkid request - # instance. - # - # session[:openid_param] is set to the openid identifier - # provided by the user. - # - # session[:return_to] is set to the return_to uri given to the - # identity provider. - - def check(consumer, session, req) - oid = consumer.begin(req.GET[@openid_param], @anonymous) - req.env['rack.auth.openid.request'] = oid - req.env['rack.errors'].puts(oid.message) - p oid if $DEBUG - - ## Extension support - extensions.each do |ext,args| - oid.add_extension(ext::Request.new(*args)) - end - - session[:openid_param] = req.GET[openid_param] - return_to_uri = return_to ? return_to : req.url - session[:return_to] = return_to_uri - immediate = session.key?(:setup_needed) ? false : immediate - - if oid.send_redirect?(realm, return_to_uri, immediate) - uri = oid.redirect_url(realm, return_to_uri, immediate) - redirect(uri) - else - confirm_post_params(oid, realm, return_to_uri, immediate) - end - rescue ::OpenID::DiscoveryFailure => e - # thrown from inside OpenID::Consumer#begin by yadis stuff - req.env['rack.errors'].puts([e.message, *e.backtrace]*"\n") - return foreign_server_failure - end - - # This is the final portion of authentication. - # If successful, a redirect to the realm is be returned. - # Data gathered from extensions are stored in session[:openid] with the - # extension's namespace uri as the key. - # - # Any messages from OpenID's response are logged to env['rack.errors'] - # - # env['rack.auth.openid.response'] will contain the openid - # response. - - def finish(consumer, session, req) - oid = consumer.complete(req.GET, req.url) - req.env['rack.auth.openid.response'] = oid - req.env['rack.errors'].puts(oid.message) - p oid if $DEBUG - - raise unless ValidStatus.include?(oid.status) - __send__(oid.status, oid, req, session) - end - - # The first argument should be the main extension module. - # The extension module should contain the constants: - # * class Request, should have OpenID::Extension as an ancestor - # * class Response, should have OpenID::Extension as an ancestor - # * string NS_URI, which defining the namespace of the extension - # - # All trailing arguments will be passed to extension::Request.new in - # #check. - # The openid response will be passed to - # extension::Response#from_success_response, #get_extension_args will be - # called on the result to attain the gathered data. - # - # This method returns the key at which the response data will be found in - # the session, which is the namespace uri by default. - - def add_extension(ext, *args) - raise BadExtension unless valid_extension?(ext) - extensions[ext] = args - return ext::NS_URI - end - - # Checks the validitity, in the context of usage, of a submitted - # extension. - - def valid_extension?(ext) - if not %w[NS_URI Request Response].all?{|c| ext.const_defined?(c) } - raise ArgumentError, 'Extension is missing constants.' - elsif not ext::Response.respond_to?(:from_success_response) - raise ArgumentError, 'Response is missing required method.' - end - return true - rescue - return false - end - - # Checks the provided uri to ensure it'd be considered within the realm. - # is currently not compatible with wildcard realms. - - def within_realm? uri - uri = URI.parse(uri.to_s) - realm = URI.parse(self.realm) - return false unless uri.absolute? - return false unless uri.path[0, realm.path.size] == realm.path - return false unless uri.host == realm.host or realm.host[/^\*\./] - # for wildcard support, is awkward with URI limitations - realm_match = Regexp.escape(realm.host). - sub(/^\*\./,"^#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+.")+'$' - return false unless uri.host.match(realm_match) - return true - end - alias_method :include?, :within_realm? - - protected - - ### These methods define some of the boilerplate responses. - - # Returns an html form page for posting to an Identity Provider if the - # GET request would exceed the upper URI length limit. - - def confirm_post_params(oid, realm, return_to, immediate) - Rack::Response.new.finish do |r| - r.write 'Confirm...' - r.write oid.form_markup(realm, return_to, immediate) - r.write '' - end - end - - # Returns a 303 redirect with the destination of that provided by the - # argument. - - def redirect(uri) - [ 303, {'Content-Length'=>'0', 'Content-Type'=>'text/plain', - 'Location' => uri}, - [] ] - end - - # Returns an empty 400 response. - - def bad_request - [ 400, {'Content-Type'=>'text/plain', 'Content-Length'=>'0'}, - [''] ] - end - - # Returns a basic unauthorized 401 response. - - def unauthorized - [ 401, {'Content-Type' => 'text/plain', 'Content-Length' => '13'}, - ['Unauthorized.'] ] - end - - # Returns a basic access denied 403 response. - - def access_denied - [ 403, {'Content-Type' => 'text/plain', 'Content-Length' => '14'}, - ['Access denied.'] ] - end - - # Returns a 503 response to be used if communication with the remote - # OpenID server fails. - - def foreign_server_failure - [ 503, {'Content-Type'=>'text/plain', 'Content-Length' => '23'}, - ['Foreign server failure.'] ] - end - - private - - ### These methods are called after a transaction is completed, depending - # on its outcome. These should all return a rack compatible response. - # You'd want to override these to provide additional functionality. - - # Called to complete processing on a successful transaction. - # Within the openid session, :openid_identity and :openid_identifier are - # set to the user friendly and the standard representation of the - # validated identity. All other data in the openid session is cleared. - - def success(oid, request, session) - session.clear - session[:openid_identity] = oid.display_identifier - session[:openid_identifier] = oid.identity_url - extensions.keys.each do |ext| - label = ext.name[/[^:]+$/].downcase - response = ext::Response.from_success_response(oid) - session[label] = response.data - end - redirect(realm) - end - - # Called if the Identity Provider indicates further setup by the user is - # required. - # The identifier is retrived from the openid session at :openid_param. - # And :setup_needed is set to true to prevent looping. - - def setup_needed(oid, request, session) - identifier = session[:openid_param] - session[:setup_needed] = true - redirect req.script_name + '?' + openid_param + '=' + identifier - end - - # Called if the user indicates they wish to cancel identification. - # Data within openid session is cleared. - - def cancel(oid, request, session) - session.clear - access_denied - end - - # Called if the Identity Provider indicates the user is unable to confirm - # their identity. Data within the openid session is left alone, in case - # of swarm auth attacks. - - def failure(oid, request, session) - unauthorized - end - end - - # A class developed out of the request to use OpenID as an authentication - # middleware. The request will be sent to the OpenID instance unless the - # block evaluates to true. For example in rackup, you can use it as such: - # - # use Rack::Session::Pool - # use Rack::Auth::OpenIDAuth, realm, openid_options do |env| - # env['rack.session'][:authkey] == a_string - # end - # run RackApp - # - # Or simply: - # - # app = Rack::Auth::OpenIDAuth.new app, realm, openid_options, &auth - - class OpenIDAuth < Rack::Auth::AbstractHandler - attr_reader :oid - def initialize(app, realm, options={}, &auth) - @oid = OpenID.new(realm, options) - super(app, &auth) - end - - def call(env) - to = auth.call(env) ? @app : @oid - to.call env - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb deleted file mode 100644 index 295235e..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb +++ /dev/null @@ -1,63 +0,0 @@ -module Rack - # Rack::Builder implements a small DSL to iteratively construct Rack - # applications. - # - # Example: - # - # app = Rack::Builder.new { - # use Rack::CommonLogger - # use Rack::ShowExceptions - # map "/lobster" do - # use Rack::Lint - # run Rack::Lobster.new - # end - # } - # - # Or - # - # app = Rack::Builder.app do - # use Rack::CommonLogger - # lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] } - # end - # - # +use+ adds a middleware to the stack, +run+ dispatches to an application. - # You can use +map+ to construct a Rack::URLMap in a convenient way. - - class Builder - def initialize(&block) - @ins = [] - instance_eval(&block) if block_given? - end - - def self.app(&block) - self.new(&block).to_app - end - - def use(middleware, *args, &block) - @ins << lambda { |app| middleware.new(app, *args, &block) } - end - - def run(app) - @ins << app #lambda { |nothing| app } - end - - def map(path, &block) - if @ins.last.kind_of? Hash - @ins.last[path] = self.class.new(&block).to_app - else - @ins << {} - map(path, &block) - end - end - - def to_app - @ins[-1] = Rack::URLMap.new(@ins.last) if Hash === @ins.last - inner_app = @ins.last - @ins[0...-1].reverse.inject(inner_app) { |a, e| e.call(a) } - end - - def call(env) - to_app.call(env) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/cascade.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/cascade.rb deleted file mode 100644 index a038aa1..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/cascade.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Rack - # Rack::Cascade tries an request on several apps, and returns the - # first response that is not 404 (or in a list of configurable - # status codes). - - class Cascade - attr_reader :apps - - def initialize(apps, catch=404) - @apps = apps - @catch = [*catch] - end - - def call(env) - status = headers = body = nil - raise ArgumentError, "empty cascade" if @apps.empty? - @apps.each { |app| - begin - status, headers, body = app.call(env) - break unless @catch.include?(status.to_i) - end - } - [status, headers, body] - end - - def add app - @apps << app - end - - def include? app - @apps.include? app - end - - alias_method :<<, :add - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb deleted file mode 100644 index 280d89d..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'rack/utils' - -module Rack - - # Middleware that applies chunked transfer encoding to response bodies - # when the response does not include a Content-Length header. - class Chunked - include Rack::Utils - - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = HeaderHash.new(headers) - - if env['HTTP_VERSION'] == 'HTTP/1.0' || - STATUS_WITH_NO_ENTITY_BODY.include?(status) || - headers['Content-Length'] || - headers['Transfer-Encoding'] - [status, headers.to_hash, body] - else - dup.chunk(status, headers, body) - end - end - - def chunk(status, headers, body) - @body = body - headers.delete('Content-Length') - headers['Transfer-Encoding'] = 'chunked' - [status, headers.to_hash, self] - end - - def each - term = "\r\n" - @body.each do |chunk| - size = bytesize(chunk) - next if size == 0 - yield [size.to_s(16), term, chunk, term].join - end - yield ["0", term, "", term].join - end - - def close - @body.close if @body.respond_to?(:close) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/commonlogger.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/commonlogger.rb deleted file mode 100644 index 5e68ac6..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/commonlogger.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Rack - # Rack::CommonLogger forwards every request to an +app+ given, and - # logs a line in the Apache common log format to the +logger+, or - # rack.errors by default. - - class CommonLogger - def initialize(app, logger=nil) - @app = app - @logger = logger - end - - def call(env) - dup._call(env) - end - - def _call(env) - @env = env - @logger ||= self - @time = Time.now - @status, @header, @body = @app.call(env) - [@status, @header, self] - end - - def close - @body.close if @body.respond_to? :close - end - - # By default, log to rack.errors. - def <<(str) - @env["rack.errors"].write(str) - @env["rack.errors"].flush - end - - def each - length = 0 - @body.each { |part| - length += part.size - yield part - } - - @now = Time.now - - # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common - # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 - - # %{%s - %s [%s] "%s %s%s %s" %d %s\n} % - @logger << %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} % - [ - @env['HTTP_X_FORWARDED_FOR'] || @env["REMOTE_ADDR"] || "-", - @env["REMOTE_USER"] || "-", - @now.strftime("%d/%b/%Y %H:%M:%S"), - @env["REQUEST_METHOD"], - @env["PATH_INFO"], - @env["QUERY_STRING"].empty? ? "" : "?"+@env["QUERY_STRING"], - @env["HTTP_VERSION"], - @status.to_s[0..3], - (length.zero? ? "-" : length.to_s), - @now - @time - ] - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/conditionalget.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/conditionalget.rb deleted file mode 100644 index 7bec824..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/conditionalget.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'rack/utils' - -module Rack - - # Middleware that enables conditional GET using If-None-Match and - # If-Modified-Since. The application should set either or both of the - # Last-Modified or Etag response headers according to RFC 2616. When - # either of the conditions is met, the response body is set to be zero - # length and the response status is set to 304 Not Modified. - # - # Applications that defer response body generation until the body's each - # message is received will avoid response body generation completely when - # a conditional GET matches. - # - # Adapted from Michael Klishin's Merb implementation: - # http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb - class ConditionalGet - def initialize(app) - @app = app - end - - def call(env) - return @app.call(env) unless %w[GET HEAD].include?(env['REQUEST_METHOD']) - - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - if etag_matches?(env, headers) || modified_since?(env, headers) - status = 304 - body = [] - end - [status, headers, body] - end - - private - def etag_matches?(env, headers) - etag = headers['Etag'] and etag == env['HTTP_IF_NONE_MATCH'] - end - - def modified_since?(env, headers) - last_modified = headers['Last-Modified'] and - last_modified == env['HTTP_IF_MODIFIED_SINCE'] - end - end - -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb deleted file mode 100644 index 1e56d43..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'rack/utils' - -module Rack - # Sets the Content-Length header on responses with fixed-length bodies. - class ContentLength - include Rack::Utils - - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = HeaderHash.new(headers) - - if !STATUS_WITH_NO_ENTITY_BODY.include?(status) && - !headers['Content-Length'] && - !headers['Transfer-Encoding'] && - (body.respond_to?(:to_ary) || body.respond_to?(:to_str)) - - body = [body] if body.respond_to?(:to_str) # rack 0.4 compat - length = body.to_ary.inject(0) { |len, part| len + bytesize(part) } - headers['Content-Length'] = length.to_s - end - - [status, headers, body] - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb deleted file mode 100644 index 0c1e1ca..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'rack/utils' - -module Rack - - # Sets the Content-Type header on responses which don't have one. - # - # Builder Usage: - # use Rack::ContentType, "text/plain" - # - # When no content type argument is provided, "text/html" is assumed. - class ContentType - def initialize(app, content_type = "text/html") - @app, @content_type = app, content_type - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - headers['Content-Type'] ||= @content_type - [status, headers.to_hash, body] - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb deleted file mode 100644 index a42b747..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb +++ /dev/null @@ -1,85 +0,0 @@ -require "zlib" -require "stringio" -require "time" # for Time.httpdate -require 'rack/utils' - -module Rack - class Deflater - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - - # Skip compressing empty entity body responses and responses with - # no-transform set. - if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) || - headers['Cache-Control'].to_s =~ /\bno-transform\b/ - return [status, headers, body] - end - - request = Request.new(env) - - encoding = Utils.select_best_encoding(%w(gzip deflate identity), - request.accept_encoding) - - # Set the Vary HTTP header. - vary = headers["Vary"].to_s.split(",").map { |v| v.strip } - unless vary.include?("*") || vary.include?("Accept-Encoding") - headers["Vary"] = vary.push("Accept-Encoding").join(",") - end - - case encoding - when "gzip" - mtime = headers.key?("Last-Modified") ? - Time.httpdate(headers["Last-Modified"]) : Time.now - body = self.class.gzip(body, mtime) - size = Rack::Utils.bytesize(body) - headers = headers.merge("Content-Encoding" => "gzip", "Content-Length" => size.to_s) - [status, headers, [body]] - when "deflate" - body = self.class.deflate(body) - size = Rack::Utils.bytesize(body) - headers = headers.merge("Content-Encoding" => "deflate", "Content-Length" => size.to_s) - [status, headers, [body]] - when "identity" - [status, headers, body] - when nil - message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found." - [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]] - end - end - - def self.gzip(body, mtime) - io = StringIO.new - gzip = Zlib::GzipWriter.new(io) - gzip.mtime = mtime - - # TODO: Add streaming - body.each { |part| gzip << part } - - gzip.close - return io.string - end - - DEFLATE_ARGS = [ - Zlib::DEFAULT_COMPRESSION, - # drop the zlib header which causes both Safari and IE to choke - -Zlib::MAX_WBITS, - Zlib::DEF_MEM_LEVEL, - Zlib::DEFAULT_STRATEGY - ] - - # Loosely based on Mongrel's Deflate handler - def self.deflate(body) - deflater = Zlib::Deflate.new(*DEFLATE_ARGS) - - # TODO: Add streaming - body.each { |part| deflater << part } - - return deflater.finish - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb deleted file mode 100644 index acdd302..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb +++ /dev/null @@ -1,153 +0,0 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' - -module Rack - # Rack::Directory serves entries below the +root+ given, according to the - # path info of the Rack request. If a directory is found, the file's contents - # will be presented in an html based index. If a file is found, the env will - # be passed to the specified +app+. - # - # If +app+ is not specified, a Rack::File of the same +root+ will be used. - - class Directory - DIR_FILE = "%s%s%s%s" - DIR_PAGE = <<-PAGE - - %s - - - -

%s

-
- - - - - - - -%s -
NameSizeTypeLast Modified
-
- - PAGE - - attr_reader :files - attr_accessor :root, :path - - def initialize(root, app=nil) - @root = F.expand_path(root) - @app = app || Rack::File.new(@root) - end - - def call(env) - dup._call(env) - end - - F = ::File - - def _call(env) - @env = env - @script_name = env['SCRIPT_NAME'] - @path_info = Utils.unescape(env['PATH_INFO']) - - if forbidden = check_forbidden - forbidden - else - @path = F.join(@root, @path_info) - list_path - end - end - - def check_forbidden - return unless @path_info.include? ".." - - body = "Forbidden\n" - size = Rack::Utils.bytesize(body) - return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]] - end - - def list_directory - @files = [['../','Parent Directory','','','']] - glob = F.join(@path, '*') - - Dir[glob].sort.each do |node| - stat = stat(node) - next unless stat - basename = F.basename(node) - ext = F.extname(node) - - url = F.join(@script_name, @path_info, basename) - size = stat.size - type = stat.directory? ? 'directory' : Mime.mime_type(ext) - size = stat.directory? ? '-' : filesize_format(size) - mtime = stat.mtime.httpdate - url << '/' if stat.directory? - basename << '/' if stat.directory? - - @files << [ url, basename, size, type, mtime ] - end - - return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ] - end - - def stat(node, max = 10) - F.stat(node) - rescue Errno::ENOENT, Errno::ELOOP - return nil - end - - # TODO: add correct response if not readable, not sure if 404 is the best - # option - def list_path - @stat = F.stat(@path) - - if @stat.readable? - return @app.call(@env) if @stat.file? - return list_directory if @stat.directory? - else - raise Errno::ENOENT, 'No such file or directory' - end - - rescue Errno::ENOENT, Errno::ELOOP - return entity_not_found - end - - def entity_not_found - body = "Entity not found: #{@path_info}\n" - size = Rack::Utils.bytesize(body) - return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]] - end - - def each - show_path = @path.sub(/^#{@root}/,'') - files = @files.map{|f| DIR_FILE % f }*"\n" - page = DIR_PAGE % [ show_path, show_path , files ] - page.each_line{|l| yield l } - end - - # Stolen from Ramaze - - FILESIZE_FORMAT = [ - ['%.1fT', 1 << 40], - ['%.1fG', 1 << 30], - ['%.1fM', 1 << 20], - ['%.1fK', 1 << 10], - ] - - def filesize_format(int) - FILESIZE_FORMAT.each do |format, size| - return format % (int.to_f / size) if int >= size - end - - int.to_s + 'B' - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb deleted file mode 100644 index fe62bd6..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' - -module Rack - # Rack::File serves files below the +root+ given, according to the - # path info of the Rack request. - # - # Handlers can detect if bodies are a Rack::File, and use mechanisms - # like sendfile on the +path+. - - class File - attr_accessor :root - attr_accessor :path - - alias :to_path :path - - def initialize(root) - @root = root - end - - def call(env) - dup._call(env) - end - - F = ::File - - def _call(env) - @path_info = Utils.unescape(env["PATH_INFO"]) - return forbidden if @path_info.include? ".." - - @path = F.join(@root, @path_info) - - begin - if F.file?(@path) && F.readable?(@path) - serving - else - raise Errno::EPERM - end - rescue SystemCallError - not_found - end - end - - def forbidden - body = "Forbidden\n" - [403, {"Content-Type" => "text/plain", - "Content-Length" => body.size.to_s}, - [body]] - end - - # NOTE: - # We check via File::size? whether this file provides size info - # via stat (e.g. /proc files often don't), otherwise we have to - # figure it out by reading the whole file into memory. And while - # we're at it we also use this as body then. - - def serving - if size = F.size?(@path) - body = self - else - body = [F.read(@path)] - size = Utils.bytesize(body.first) - end - - [200, { - "Last-Modified" => F.mtime(@path).httpdate, - "Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain'), - "Content-Length" => size.to_s - }, body] - end - - def not_found - body = "File not found: #{@path_info}\n" - [404, {"Content-Type" => "text/plain", - "Content-Length" => body.size.to_s}, - [body]] - end - - def each - F.open(@path, "rb") { |file| - while part = file.read(8192) - yield part - end - } - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler.rb deleted file mode 100644 index 1018af6..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler.rb +++ /dev/null @@ -1,48 +0,0 @@ -module Rack - # *Handlers* connect web servers with Rack. - # - # Rack includes Handlers for Mongrel, WEBrick, FastCGI, CGI, SCGI - # and LiteSpeed. - # - # Handlers usually are activated by calling MyHandler.run(myapp). - # A second optional hash can be passed to include server-specific - # configuration. - module Handler - def self.get(server) - return unless server - - if klass = @handlers[server] - obj = Object - klass.split("::").each { |x| obj = obj.const_get(x) } - obj - else - Rack::Handler.const_get(server.capitalize) - end - end - - def self.register(server, klass) - @handlers ||= {} - @handlers[server] = klass - end - - autoload :CGI, "rack/handler/cgi" - autoload :FastCGI, "rack/handler/fastcgi" - autoload :Mongrel, "rack/handler/mongrel" - autoload :EventedMongrel, "rack/handler/evented_mongrel" - autoload :SwiftipliedMongrel, "rack/handler/swiftiplied_mongrel" - autoload :WEBrick, "rack/handler/webrick" - autoload :LSWS, "rack/handler/lsws" - autoload :SCGI, "rack/handler/scgi" - autoload :Thin, "rack/handler/thin" - - register 'cgi', 'Rack::Handler::CGI' - register 'fastcgi', 'Rack::Handler::FastCGI' - register 'mongrel', 'Rack::Handler::Mongrel' - register 'emongrel', 'Rack::Handler::EventedMongrel' - register 'smongrel', 'Rack::Handler::SwiftipliedMongrel' - register 'webrick', 'Rack::Handler::WEBrick' - register 'lsws', 'Rack::Handler::LSWS' - register 'scgi', 'Rack::Handler::SCGI' - register 'thin', 'Rack::Handler::Thin' - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb deleted file mode 100644 index e38156c..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'rack/content_length' - -module Rack - module Handler - class CGI - def self.run(app, options=nil) - serve app - end - - def self.serve(app) - app = ContentLength.new(app) - - env = ENV.to_hash - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - env.update({"rack.version" => [0,1], - "rack.input" => $stdin, - "rack.errors" => $stderr, - - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => true, - - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - - status, headers, body = app.call(env) - begin - send_headers status, headers - send_body body - ensure - body.close if body.respond_to? :close - end - end - - def self.send_headers(status, headers) - STDOUT.print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - STDOUT.print "#{k}: #{v}\r\n" - } - } - STDOUT.print "\r\n" - STDOUT.flush - end - - def self.send_body(body) - body.each { |part| - STDOUT.print part - STDOUT.flush - } - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/evented_mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/evented_mongrel.rb deleted file mode 100644 index 0f5cbf7..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/evented_mongrel.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'swiftcore/evented_mongrel' - -module Rack - module Handler - class EventedMongrel < Handler::Mongrel - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb deleted file mode 100644 index 6324c7d..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'fcgi' -require 'socket' -require 'rack/content_length' - -module Rack - module Handler - class FastCGI - def self.run(app, options={}) - file = options[:File] and STDIN.reopen(UNIXServer.new(file)) - port = options[:Port] and STDIN.reopen(TCPServer.new(port)) - FCGI.each { |request| - serve request, app - } - end - - module ProperStream # :nodoc: - def each # This is missing by default. - while line = gets - yield line - end - end - - def read(*args) - if args.empty? - super || "" # Empty string on EOF. - else - super - end - end - end - - def self.serve(request, app) - app = Rack::ContentLength.new(app) - - env = request.env - env.delete "HTTP_CONTENT_LENGTH" - - request.in.extend ProperStream - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - env.update({"rack.version" => [0,1], - "rack.input" => request.in, - "rack.errors" => request.err, - - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" - }) - - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - env.delete "PATH_INFO" if env["PATH_INFO"] == "" - env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == "" - env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == "" - - status, headers, body = app.call(env) - begin - send_headers request.out, status, headers - send_body request.out, body - ensure - body.close if body.respond_to? :close - request.finish - end - end - - def self.send_headers(out, status, headers) - out.print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - out.print "#{k}: #{v}\r\n" - } - } - out.print "\r\n" - out.flush - end - - def self.send_body(out, body) - body.each { |part| - out.print part - out.flush - } - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb deleted file mode 100644 index c65ba3e..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'lsapi' -require 'rack/content_length' - -module Rack - module Handler - class LSWS - def self.run(app, options=nil) - while LSAPI.accept != nil - serve app - end - end - def self.serve(app) - app = Rack::ContentLength.new(app) - - env = ENV.to_hash - env.delete "HTTP_CONTENT_LENGTH" - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - env.update({"rack.version" => [0,1], - "rack.input" => StringIO.new($stdin.read.to_s), - "rack.errors" => $stderr, - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => false, - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - status, headers, body = app.call(env) - begin - send_headers status, headers - send_body body - ensure - body.close if body.respond_to? :close - end - end - def self.send_headers(status, headers) - print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - print "#{k}: #{v}\r\n" - } - } - print "\r\n" - STDOUT.flush - end - def self.send_body(body) - body.each { |part| - print part - STDOUT.flush - } - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb deleted file mode 100644 index f0c0d58..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'mongrel' -require 'stringio' -require 'rack/content_length' -require 'rack/chunked' - -module Rack - module Handler - class Mongrel < ::Mongrel::HttpHandler - def self.run(app, options={}) - server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0', - options[:Port] || 8080) - # Acts like Rack::URLMap, utilizing Mongrel's own path finding methods. - # Use is similar to #run, replacing the app argument with a hash of - # { path=>app, ... } or an instance of Rack::URLMap. - if options[:map] - if app.is_a? Hash - app.each do |path, appl| - path = '/'+path unless path[0] == ?/ - server.register(path, Rack::Handler::Mongrel.new(appl)) - end - elsif app.is_a? URLMap - app.instance_variable_get(:@mapping).each do |(host, path, appl)| - next if !host.nil? && !options[:Host].nil? && options[:Host] != host - path = '/'+path unless path[0] == ?/ - server.register(path, Rack::Handler::Mongrel.new(appl)) - end - else - raise ArgumentError, "first argument should be a Hash or URLMap" - end - else - server.register('/', Rack::Handler::Mongrel.new(app)) - end - yield server if block_given? - server.run.join - end - - def initialize(app) - @app = Rack::Chunked.new(Rack::ContentLength.new(app)) - end - - def process(request, response) - env = {}.replace(request.params) - env.delete "HTTP_CONTENT_TYPE" - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - env.update({"rack.version" => [0,1], - "rack.input" => request.body || StringIO.new(""), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => false, # ??? - "rack.run_once" => false, - - "rack.url_scheme" => "http", - }) - env["QUERY_STRING"] ||= "" - env.delete "PATH_INFO" if env["PATH_INFO"] == "" - - status, headers, body = @app.call(env) - - begin - response.status = status.to_i - response.send_status(nil) - - headers.each { |k, vs| - vs.split("\n").each { |v| - response.header[k] = v - } - } - response.send_header - - body.each { |part| - response.write part - response.socket.flush - } - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb deleted file mode 100644 index 9495c66..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'scgi' -require 'stringio' -require 'rack/content_length' -require 'rack/chunked' - -module Rack - module Handler - class SCGI < ::SCGI::Processor - attr_accessor :app - - def self.run(app, options=nil) - new(options.merge(:app=>app, - :host=>options[:Host], - :port=>options[:Port], - :socket=>options[:Socket])).listen - end - - def initialize(settings = {}) - @app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app])) - @log = Object.new - def @log.info(*args); end - def @log.error(*args); end - super(settings) - end - - def process_request(request, input_body, socket) - env = {}.replace(request) - env.delete "HTTP_CONTENT_TYPE" - env.delete "HTTP_CONTENT_LENGTH" - env["REQUEST_PATH"], env["QUERY_STRING"] = env["REQUEST_URI"].split('?', 2) - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["PATH_INFO"] = env["REQUEST_PATH"] - env["QUERY_STRING"] ||= "" - env["SCRIPT_NAME"] = "" - env.update({"rack.version" => [0,1], - "rack.input" => StringIO.new(input_body), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => true, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" - }) - status, headers, body = app.call(env) - begin - socket.write("Status: #{status}\r\n") - headers.each do |k, vs| - vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")} - end - socket.write("\r\n") - body.each {|s| socket.write(s)} - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/swiftiplied_mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/swiftiplied_mongrel.rb deleted file mode 100644 index 4bafd0b..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/swiftiplied_mongrel.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'swiftcore/swiftiplied_mongrel' - -module Rack - module Handler - class SwiftipliedMongrel < Handler::Mongrel - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb deleted file mode 100644 index 3d4fedf..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb +++ /dev/null @@ -1,18 +0,0 @@ -require "thin" -require "rack/content_length" -require "rack/chunked" - -module Rack - module Handler - class Thin - def self.run(app, options={}) - app = Rack::Chunked.new(Rack::ContentLength.new(app)) - server = ::Thin::Server.new(options[:Host] || '0.0.0.0', - options[:Port] || 8080, - app) - yield server if block_given? - server.start - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb deleted file mode 100644 index 829e7d6..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'webrick' -require 'stringio' -require 'rack/content_length' - -module Rack - module Handler - class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet - def self.run(app, options={}) - server = ::WEBrick::HTTPServer.new(options) - server.mount "/", Rack::Handler::WEBrick, app - trap(:INT) { server.shutdown } - yield server if block_given? - server.start - end - - def initialize(server, app) - super server - @app = Rack::ContentLength.new(app) - end - - def service(req, res) - env = req.meta_vars - env.delete_if { |k, v| v.nil? } - - env.update({"rack.version" => [0,1], - "rack.input" => StringIO.new(req.body.to_s), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => false, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["QUERY_STRING"] ||= "" - env["REQUEST_PATH"] ||= "/" - if env["PATH_INFO"] == "" - env.delete "PATH_INFO" - else - path, n = req.request_uri.path, env["SCRIPT_NAME"].length - env["PATH_INFO"] = path[n, path.length-n] - end - - status, headers, body = @app.call(env) - begin - res.status = status.to_i - headers.each { |k, vs| - if k.downcase == "set-cookie" - res.cookies.concat vs.split("\n") - else - vs.split("\n").each { |v| - res[k] = v - } - end - } - body.each { |part| - res.body << part - } - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/head.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/head.rb deleted file mode 100644 index deab822..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/head.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Rack - -class Head - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - - if env["REQUEST_METHOD"] == "HEAD" - [status, headers, []] - else - [status, headers, body] - end - end -end - -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb deleted file mode 100644 index 44a33ce..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb +++ /dev/null @@ -1,462 +0,0 @@ -require 'rack/utils' - -module Rack - # Rack::Lint validates your application and the requests and - # responses according to the Rack spec. - - class Lint - def initialize(app) - @app = app - end - - # :stopdoc: - - class LintError < RuntimeError; end - module Assertion - def assert(message, &block) - unless block.call - raise LintError, message - end - end - end - include Assertion - - ## This specification aims to formalize the Rack protocol. You - ## can (and should) use Rack::Lint to enforce it. - ## - ## When you develop middleware, be sure to add a Lint before and - ## after to catch all mistakes. - - ## = Rack applications - - ## A Rack application is an Ruby object (not a class) that - ## responds to +call+. - def call(env=nil) - dup._call(env) - end - - def _call(env) - ## It takes exactly one argument, the *environment* - assert("No env given") { env } - check_env env - - env['rack.input'] = InputWrapper.new(env['rack.input']) - env['rack.errors'] = ErrorWrapper.new(env['rack.errors']) - - ## and returns an Array of exactly three values: - status, headers, @body = @app.call(env) - ## The *status*, - check_status status - ## the *headers*, - check_headers headers - ## and the *body*. - check_content_type status, headers - check_content_length status, headers, env - [status, headers, self] - end - - ## == The Environment - def check_env(env) - ## The environment must be an true instance of Hash (no - ## subclassing allowed) that includes CGI-like headers. - ## The application is free to modify the environment. - assert("env #{env.inspect} is not a Hash, but #{env.class}") { - env.instance_of? Hash - } - - ## - ## The environment is required to include these variables - ## (adopted from PEP333), except when they'd be empty, but see - ## below. - - ## REQUEST_METHOD:: The HTTP request method, such as - ## "GET" or "POST". This cannot ever - ## be an empty string, and so is - ## always required. - - ## SCRIPT_NAME:: The initial portion of the request - ## URL's "path" that corresponds to the - ## application object, so that the - ## application knows its virtual - ## "location". This may be an empty - ## string, if the application corresponds - ## to the "root" of the server. - - ## PATH_INFO:: The remainder of the request URL's - ## "path", designating the virtual - ## "location" of the request's target - ## within the application. This may be an - ## empty string, if the request URL targets - ## the application root and does not have a - ## trailing slash. This information should be - ## decoded by the server if it comes from a - ## URL. - - ## QUERY_STRING:: The portion of the request URL that - ## follows the ?, if any. May be - ## empty, but is always required! - - ## SERVER_NAME, SERVER_PORT:: When combined with SCRIPT_NAME and PATH_INFO, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to SERVER_NAME for reconstructing the request URL. SERVER_NAME and SERVER_PORT can never be empty strings, and so are always required. - - ## HTTP_ Variables:: Variables corresponding to the - ## client-supplied HTTP request - ## headers (i.e., variables whose - ## names begin with HTTP_). The - ## presence or absence of these - ## variables should correspond with - ## the presence or absence of the - ## appropriate HTTP header in the - ## request. - - ## In addition to this, the Rack environment must include these - ## Rack-specific variables: - - ## rack.version:: The Array [0,1], representing this version of Rack. - ## rack.url_scheme:: +http+ or +https+, depending on the request URL. - ## rack.input:: See below, the input stream. - ## rack.errors:: See below, the error stream. - ## rack.multithread:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise. - ## rack.multiprocess:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise. - ## rack.run_once:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar). - - ## The server or the application can store their own data in the - ## environment, too. The keys must contain at least one dot, - ## and should be prefixed uniquely. The prefix rack. - ## is reserved for use with the Rack core distribution and must - ## not be used otherwise. - ## - - %w[REQUEST_METHOD SERVER_NAME SERVER_PORT - QUERY_STRING - rack.version rack.input rack.errors - rack.multithread rack.multiprocess rack.run_once].each { |header| - assert("env missing required key #{header}") { env.include? header } - } - - ## The environment must not contain the keys - ## HTTP_CONTENT_TYPE or HTTP_CONTENT_LENGTH - ## (use the versions without HTTP_). - %w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header| - assert("env contains #{header}, must use #{header[5,-1]}") { - not env.include? header - } - } - - ## The CGI keys (named without a period) must have String values. - env.each { |key, value| - next if key.include? "." # Skip extensions - assert("env variable #{key} has non-string value #{value.inspect}") { - value.instance_of? String - } - } - - ## - ## There are the following restrictions: - - ## * rack.version must be an array of Integers. - assert("rack.version must be an Array, was #{env["rack.version"].class}") { - env["rack.version"].instance_of? Array - } - ## * rack.url_scheme must either be +http+ or +https+. - assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") { - %w[http https].include? env["rack.url_scheme"] - } - - ## * There must be a valid input stream in rack.input. - check_input env["rack.input"] - ## * There must be a valid error stream in rack.errors. - check_error env["rack.errors"] - - ## * The REQUEST_METHOD must be a valid token. - assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") { - env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/ - } - - ## * The SCRIPT_NAME, if non-empty, must start with / - assert("SCRIPT_NAME must start with /") { - !env.include?("SCRIPT_NAME") || - env["SCRIPT_NAME"] == "" || - env["SCRIPT_NAME"] =~ /\A\// - } - ## * The PATH_INFO, if non-empty, must start with / - assert("PATH_INFO must start with /") { - !env.include?("PATH_INFO") || - env["PATH_INFO"] == "" || - env["PATH_INFO"] =~ /\A\// - } - ## * The CONTENT_LENGTH, if given, must consist of digits only. - assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") { - !env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/ - } - - ## * One of SCRIPT_NAME or PATH_INFO must be - ## set. PATH_INFO should be / if - ## SCRIPT_NAME is empty. - assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") { - env["SCRIPT_NAME"] || env["PATH_INFO"] - } - ## SCRIPT_NAME never should be /, but instead be empty. - assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") { - env["SCRIPT_NAME"] != "/" - } - end - - ## === The Input Stream - def check_input(input) - ## The input stream must respond to +gets+, +each+ and +read+. - [:gets, :each, :read].each { |method| - assert("rack.input #{input} does not respond to ##{method}") { - input.respond_to? method - } - } - end - - class InputWrapper - include Assertion - - def initialize(input) - @input = input - end - - def size - @input.size - end - - def rewind - @input.rewind - end - - ## * +gets+ must be called without arguments and return a string, - ## or +nil+ on EOF. - def gets(*args) - assert("rack.input#gets called with arguments") { args.size == 0 } - v = @input.gets - assert("rack.input#gets didn't return a String") { - v.nil? or v.instance_of? String - } - v - end - - ## * +read+ must be called without or with one integer argument - ## and return a string, or +nil+ on EOF. - def read(*args) - assert("rack.input#read called with too many arguments") { - args.size <= 1 - } - if args.size == 1 - assert("rack.input#read called with non-integer argument") { - args.first.kind_of? Integer - } - end - v = @input.read(*args) - assert("rack.input#read didn't return a String") { - v.nil? or v.instance_of? String - } - v - end - - ## * +each+ must be called without arguments and only yield Strings. - def each(*args) - assert("rack.input#each called with arguments") { args.size == 0 } - @input.each { |line| - assert("rack.input#each didn't yield a String") { - line.instance_of? String - } - yield line - } - end - - ## * +close+ must never be called on the input stream. - def close(*args) - assert("rack.input#close must not be called") { false } - end - end - - ## === The Error Stream - def check_error(error) - ## The error stream must respond to +puts+, +write+ and +flush+. - [:puts, :write, :flush].each { |method| - assert("rack.error #{error} does not respond to ##{method}") { - error.respond_to? method - } - } - end - - class ErrorWrapper - include Assertion - - def initialize(error) - @error = error - end - - ## * +puts+ must be called with a single argument that responds to +to_s+. - def puts(str) - @error.puts str - end - - ## * +write+ must be called with a single argument that is a String. - def write(str) - assert("rack.errors#write not called with a String") { str.instance_of? String } - @error.write str - end - - ## * +flush+ must be called without arguments and must be called - ## in order to make the error appear for sure. - def flush - @error.flush - end - - ## * +close+ must never be called on the error stream. - def close(*args) - assert("rack.errors#close must not be called") { false } - end - end - - ## == The Response - - ## === The Status - def check_status(status) - ## The status, if parsed as integer (+to_i+), must be greater than or equal to 100. - assert("Status must be >=100 seen as integer") { status.to_i >= 100 } - end - - ## === The Headers - def check_headers(header) - ## The header must respond to each, and yield values of key and value. - assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") { - header.respond_to? :each - } - header.each { |key, value| - ## The header keys must be Strings. - assert("header key must be a string, was #{key.class}") { - key.instance_of? String - } - ## The header must not contain a +Status+ key, - assert("header must not contain Status") { key.downcase != "status" } - ## contain keys with : or newlines in their name, - assert("header names must not contain : or \\n") { key !~ /[:\n]/ } - ## contain keys names that end in - or _, - assert("header names must not end in - or _") { key !~ /[-_]\z/ } - ## but only contain keys that consist of - ## letters, digits, _ or - and start with a letter. - assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ } - - ## The values of the header must be Strings, - assert("a header value must be a String, but the value of " + - "'#{key}' is a #{value.class}") { value.kind_of? String } - ## consisting of lines (for multiple header values) seperated by "\n". - value.split("\n").each { |item| - ## The lines must not contain characters below 037. - assert("invalid header value #{key}: #{item.inspect}") { - item !~ /[\000-\037]/ - } - } - } - end - - ## === The Content-Type - def check_content_type(status, headers) - headers.each { |key, value| - ## There must be a Content-Type, except when the - ## +Status+ is 1xx, 204 or 304, in which case there must be none - ## given. - if key.downcase == "content-type" - assert("Content-Type header found in #{status} response, not allowed") { - not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - return - end - } - assert("No Content-Type header found") { - Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - end - - ## === The Content-Length - def check_content_length(status, headers, env) - headers.each { |key, value| - if key.downcase == 'content-length' - ## There must not be a Content-Length header when the - ## +Status+ is 1xx, 204 or 304. - assert("Content-Length header found in #{status} response, not allowed") { - not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - - bytes = 0 - string_body = true - - if @body.respond_to?(:to_ary) - @body.each { |part| - unless part.kind_of?(String) - string_body = false - break - end - - bytes += Rack::Utils.bytesize(part) - } - - if env["REQUEST_METHOD"] == "HEAD" - assert("Response body was given for HEAD request, but should be empty") { - bytes == 0 - } - else - if string_body - assert("Content-Length header was #{value}, but should be #{bytes}") { - value == bytes.to_s - } - end - end - end - - return - end - } - end - - ## === The Body - def each - @closed = false - ## The Body must respond to #each - @body.each { |part| - ## and must only yield String values. - assert("Body yielded non-string value #{part.inspect}") { - part.instance_of? String - } - yield part - } - ## - ## If the Body responds to #close, it will be called after iteration. - # XXX howto: assert("Body has not been closed") { @closed } - - - ## - ## If the Body responds to #to_path, it must return a String - ## identifying the location of a file whose contents are identical - ## to that produced by calling #each. - - if @body.respond_to?(:to_path) - assert("The file identified by body.to_path does not exist") { - ::File.exist? @body.to_path - } - end - - ## - ## The Body commonly is an Array of Strings, the application - ## instance itself, or a File-like object. - end - - def close - @closed = true - @body.close if @body.respond_to?(:close) - end - - # :startdoc: - - end -end - -## == Thanks -## Some parts of this specification are adopted from PEP333: Python -## Web Server Gateway Interface -## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank -## everyone involved in that effort. diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/lobster.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/lobster.rb deleted file mode 100644 index f63f419..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/lobster.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'zlib' - -require 'rack/request' -require 'rack/response' - -module Rack - # Paste has a Pony, Rack has a Lobster! - class Lobster - LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2 - P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0 - t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ - I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0]) - - LambdaLobster = lambda { |env| - if env["QUERY_STRING"].include?("flip") - lobster = LobsterString.split("\n"). - map { |line| line.ljust(42).reverse }. - join("\n") - href = "?" - else - lobster = LobsterString - href = "?flip" - end - - content = ["Lobstericious!", - "
", lobster, "
", - "flip!"] - length = content.inject(0) { |a,e| a+e.size }.to_s - [200, {"Content-Type" => "text/html", "Content-Length" => length}, content] - } - - def call(env) - req = Request.new(env) - if req.GET["flip"] == "left" - lobster = LobsterString.split("\n"). - map { |line| line.ljust(42).reverse }. - join("\n") - href = "?flip=right" - elsif req.GET["flip"] == "crash" - raise "Lobster crashed" - else - lobster = LobsterString - href = "?flip=left" - end - - res = Response.new - res.write "Lobstericious!" - res.write "
"
-      res.write lobster
-      res.write "
" - res.write "

flip!

" - res.write "

crash!

" - res.finish - end - - end -end - -if $0 == __FILE__ - require 'rack' - require 'rack/showexceptions' - Rack::Handler::WEBrick.run \ - Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), - :Port => 9292 -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/lock.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/lock.rb deleted file mode 100644 index 9323852..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/lock.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Rack - class Lock - FLAG = 'rack.multithread'.freeze - - def initialize(app, lock = Mutex.new) - @app, @lock = app, lock - end - - def call(env) - old, env[FLAG] = env[FLAG], false - @lock.synchronize { @app.call(env) } - ensure - env[FLAG] = old - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/methodoverride.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/methodoverride.rb deleted file mode 100644 index 0eed29f..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/methodoverride.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Rack - class MethodOverride - HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS) - - METHOD_OVERRIDE_PARAM_KEY = "_method".freeze - HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze - - def initialize(app) - @app = app - end - - def call(env) - if env["REQUEST_METHOD"] == "POST" - req = Request.new(env) - method = req.POST[METHOD_OVERRIDE_PARAM_KEY] || - env[HTTP_METHOD_OVERRIDE_HEADER] - method = method.to_s.upcase - if HTTP_METHODS.include?(method) - env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"] - env["REQUEST_METHOD"] = method - end - end - - @app.call(env) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/mime.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/mime.rb deleted file mode 100644 index 5a6a73a..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/mime.rb +++ /dev/null @@ -1,204 +0,0 @@ -module Rack - module Mime - # Returns String with mime type if found, otherwise use +fallback+. - # +ext+ should be filename extension in the '.ext' format that - # File.extname(file) returns. - # +fallback+ may be any object - # - # Also see the documentation for MIME_TYPES - # - # Usage: - # Rack::Mime.mime_type('.foo') - # - # This is a shortcut for: - # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream') - - def mime_type(ext, fallback='application/octet-stream') - MIME_TYPES.fetch(ext, fallback) - end - module_function :mime_type - - # List of most common mime-types, selected various sources - # according to their usefulness in a webserving scope for Ruby - # users. - # - # To amend this list with your local mime.types list you can use: - # - # require 'webrick/httputils' - # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types') - # Rack::Mime::MIME_TYPES.merge!(list) - # - # To add the list mongrel provides, use: - # - # require 'mongrel/handlers' - # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES) - - MIME_TYPES = { - ".3gp" => "video/3gpp", - ".a" => "application/octet-stream", - ".ai" => "application/postscript", - ".aif" => "audio/x-aiff", - ".aiff" => "audio/x-aiff", - ".asc" => "application/pgp-signature", - ".asf" => "video/x-ms-asf", - ".asm" => "text/x-asm", - ".asx" => "video/x-ms-asf", - ".atom" => "application/atom+xml", - ".au" => "audio/basic", - ".avi" => "video/x-msvideo", - ".bat" => "application/x-msdownload", - ".bin" => "application/octet-stream", - ".bmp" => "image/bmp", - ".bz2" => "application/x-bzip2", - ".c" => "text/x-c", - ".cab" => "application/vnd.ms-cab-compressed", - ".cc" => "text/x-c", - ".chm" => "application/vnd.ms-htmlhelp", - ".class" => "application/octet-stream", - ".com" => "application/x-msdownload", - ".conf" => "text/plain", - ".cpp" => "text/x-c", - ".crt" => "application/x-x509-ca-cert", - ".css" => "text/css", - ".csv" => "text/csv", - ".cxx" => "text/x-c", - ".deb" => "application/x-debian-package", - ".der" => "application/x-x509-ca-cert", - ".diff" => "text/x-diff", - ".djv" => "image/vnd.djvu", - ".djvu" => "image/vnd.djvu", - ".dll" => "application/x-msdownload", - ".dmg" => "application/octet-stream", - ".doc" => "application/msword", - ".dot" => "application/msword", - ".dtd" => "application/xml-dtd", - ".dvi" => "application/x-dvi", - ".ear" => "application/java-archive", - ".eml" => "message/rfc822", - ".eps" => "application/postscript", - ".exe" => "application/x-msdownload", - ".f" => "text/x-fortran", - ".f77" => "text/x-fortran", - ".f90" => "text/x-fortran", - ".flv" => "video/x-flv", - ".for" => "text/x-fortran", - ".gem" => "application/octet-stream", - ".gemspec" => "text/x-script.ruby", - ".gif" => "image/gif", - ".gz" => "application/x-gzip", - ".h" => "text/x-c", - ".hh" => "text/x-c", - ".htm" => "text/html", - ".html" => "text/html", - ".ico" => "image/vnd.microsoft.icon", - ".ics" => "text/calendar", - ".ifb" => "text/calendar", - ".iso" => "application/octet-stream", - ".jar" => "application/java-archive", - ".java" => "text/x-java-source", - ".jnlp" => "application/x-java-jnlp-file", - ".jpeg" => "image/jpeg", - ".jpg" => "image/jpeg", - ".js" => "application/javascript", - ".json" => "application/json", - ".log" => "text/plain", - ".m3u" => "audio/x-mpegurl", - ".m4v" => "video/mp4", - ".man" => "text/troff", - ".mathml" => "application/mathml+xml", - ".mbox" => "application/mbox", - ".mdoc" => "text/troff", - ".me" => "text/troff", - ".mid" => "audio/midi", - ".midi" => "audio/midi", - ".mime" => "message/rfc822", - ".mml" => "application/mathml+xml", - ".mng" => "video/x-mng", - ".mov" => "video/quicktime", - ".mp3" => "audio/mpeg", - ".mp4" => "video/mp4", - ".mp4v" => "video/mp4", - ".mpeg" => "video/mpeg", - ".mpg" => "video/mpeg", - ".ms" => "text/troff", - ".msi" => "application/x-msdownload", - ".odp" => "application/vnd.oasis.opendocument.presentation", - ".ods" => "application/vnd.oasis.opendocument.spreadsheet", - ".odt" => "application/vnd.oasis.opendocument.text", - ".ogg" => "application/ogg", - ".p" => "text/x-pascal", - ".pas" => "text/x-pascal", - ".pbm" => "image/x-portable-bitmap", - ".pdf" => "application/pdf", - ".pem" => "application/x-x509-ca-cert", - ".pgm" => "image/x-portable-graymap", - ".pgp" => "application/pgp-encrypted", - ".pkg" => "application/octet-stream", - ".pl" => "text/x-script.perl", - ".pm" => "text/x-script.perl-module", - ".png" => "image/png", - ".pnm" => "image/x-portable-anymap", - ".ppm" => "image/x-portable-pixmap", - ".pps" => "application/vnd.ms-powerpoint", - ".ppt" => "application/vnd.ms-powerpoint", - ".ps" => "application/postscript", - ".psd" => "image/vnd.adobe.photoshop", - ".py" => "text/x-script.python", - ".qt" => "video/quicktime", - ".ra" => "audio/x-pn-realaudio", - ".rake" => "text/x-script.ruby", - ".ram" => "audio/x-pn-realaudio", - ".rar" => "application/x-rar-compressed", - ".rb" => "text/x-script.ruby", - ".rdf" => "application/rdf+xml", - ".roff" => "text/troff", - ".rpm" => "application/x-redhat-package-manager", - ".rss" => "application/rss+xml", - ".rtf" => "application/rtf", - ".ru" => "text/x-script.ruby", - ".s" => "text/x-asm", - ".sgm" => "text/sgml", - ".sgml" => "text/sgml", - ".sh" => "application/x-sh", - ".sig" => "application/pgp-signature", - ".snd" => "audio/basic", - ".so" => "application/octet-stream", - ".svg" => "image/svg+xml", - ".svgz" => "image/svg+xml", - ".swf" => "application/x-shockwave-flash", - ".t" => "text/troff", - ".tar" => "application/x-tar", - ".tbz" => "application/x-bzip-compressed-tar", - ".tcl" => "application/x-tcl", - ".tex" => "application/x-tex", - ".texi" => "application/x-texinfo", - ".texinfo" => "application/x-texinfo", - ".text" => "text/plain", - ".tif" => "image/tiff", - ".tiff" => "image/tiff", - ".torrent" => "application/x-bittorrent", - ".tr" => "text/troff", - ".txt" => "text/plain", - ".vcf" => "text/x-vcard", - ".vcs" => "text/x-vcalendar", - ".vrml" => "model/vrml", - ".war" => "application/java-archive", - ".wav" => "audio/x-wav", - ".wma" => "audio/x-ms-wma", - ".wmv" => "video/x-ms-wmv", - ".wmx" => "video/x-ms-wmx", - ".wrl" => "model/vrml", - ".wsdl" => "application/wsdl+xml", - ".xbm" => "image/x-xbitmap", - ".xhtml" => "application/xhtml+xml", - ".xls" => "application/vnd.ms-excel", - ".xml" => "application/xml", - ".xpm" => "image/x-xpixmap", - ".xsl" => "application/xml", - ".xslt" => "application/xslt+xml", - ".yaml" => "text/yaml", - ".yml" => "text/yaml", - ".zip" => "application/zip", - } - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/mock.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/mock.rb deleted file mode 100644 index 70852da..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/mock.rb +++ /dev/null @@ -1,160 +0,0 @@ -require 'uri' -require 'stringio' -require 'rack/lint' -require 'rack/utils' -require 'rack/response' - -module Rack - # Rack::MockRequest helps testing your Rack application without - # actually using HTTP. - # - # After performing a request on a URL with get/post/put/delete, it - # returns a MockResponse with useful helper methods for effective - # testing. - # - # You can pass a hash with additional configuration to the - # get/post/put/delete. - # :input:: A String or IO-like to be used as rack.input. - # :fatal:: Raise a FatalWarning if the app writes to rack.errors. - # :lint:: If true, wrap the application in a Rack::Lint. - - class MockRequest - class FatalWarning < RuntimeError - end - - class FatalWarner - def puts(warning) - raise FatalWarning, warning - end - - def write(warning) - raise FatalWarning, warning - end - - def flush - end - - def string - "" - end - end - - DEFAULT_ENV = { - "rack.version" => [0,1], - "rack.input" => StringIO.new, - "rack.errors" => StringIO.new, - "rack.multithread" => true, - "rack.multiprocess" => true, - "rack.run_once" => false, - } - - def initialize(app) - @app = app - end - - def get(uri, opts={}) request("GET", uri, opts) end - def post(uri, opts={}) request("POST", uri, opts) end - def put(uri, opts={}) request("PUT", uri, opts) end - def delete(uri, opts={}) request("DELETE", uri, opts) end - - def request(method="GET", uri="", opts={}) - env = self.class.env_for(uri, opts.merge(:method => method)) - - if opts[:lint] - app = Rack::Lint.new(@app) - else - app = @app - end - - errors = env["rack.errors"] - MockResponse.new(*(app.call(env) + [errors])) - end - - # Return the Rack environment used for a request to +uri+. - def self.env_for(uri="", opts={}) - uri = URI(uri) - env = DEFAULT_ENV.dup - - env["REQUEST_METHOD"] = opts[:method] || "GET" - env["SERVER_NAME"] = uri.host || "example.org" - env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80" - env["QUERY_STRING"] = uri.query.to_s - env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path - env["rack.url_scheme"] = uri.scheme || "http" - - env["SCRIPT_NAME"] = opts[:script_name] || "" - - if opts[:fatal] - env["rack.errors"] = FatalWarner.new - else - env["rack.errors"] = StringIO.new - end - - opts[:input] ||= "" - if String === opts[:input] - env["rack.input"] = StringIO.new(opts[:input]) - else - env["rack.input"] = opts[:input] - end - - env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s - - opts.each { |field, value| - env[field] = value if String === field - } - - env - end - end - - # Rack::MockResponse provides useful helpers for testing your apps. - # Usually, you don't create the MockResponse on your own, but use - # MockRequest. - - class MockResponse - def initialize(status, headers, body, errors=StringIO.new("")) - @status = status.to_i - - @original_headers = headers - @headers = Rack::Utils::HeaderHash.new - headers.each { |field, values| - @headers[field] = values - @headers[field] = "" if values.empty? - } - - @body = "" - body.each { |part| @body << part } - - @errors = errors.string - end - - # Status - attr_reader :status - - # Headers - attr_reader :headers, :original_headers - - def [](field) - headers[field] - end - - - # Body - attr_reader :body - - def =~(other) - @body =~ other - end - - def match(other) - @body.match other - end - - - # Errors - attr_accessor :errors - - - include Response::Helpers - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/recursive.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/recursive.rb deleted file mode 100644 index bf8b965..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/recursive.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'uri' - -module Rack - # Rack::ForwardRequest gets caught by Rack::Recursive and redirects - # the current request to the app at +url+. - # - # raise ForwardRequest.new("/not-found") - # - - class ForwardRequest < Exception - attr_reader :url, :env - - def initialize(url, env={}) - @url = URI(url) - @env = env - - @env["PATH_INFO"] = @url.path - @env["QUERY_STRING"] = @url.query if @url.query - @env["HTTP_HOST"] = @url.host if @url.host - @env["HTTP_PORT"] = @url.port if @url.port - @env["rack.url_scheme"] = @url.scheme if @url.scheme - - super "forwarding to #{url}" - end - end - - # Rack::Recursive allows applications called down the chain to - # include data from other applications (by using - # rack['rack.recursive.include'][...] or raise a - # ForwardRequest to redirect internally. - - class Recursive - def initialize(app) - @app = app - end - - def call(env) - @script_name = env["SCRIPT_NAME"] - @app.call(env.merge('rack.recursive.include' => method(:include))) - rescue ForwardRequest => req - call(env.merge(req.env)) - end - - def include(env, path) - unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ || - path[@script_name.size].nil?) - raise ArgumentError, "can only include below #{@script_name}, not #{path}" - end - - env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name, - "REQUEST_METHOD" => "GET", - "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "", - "rack.input" => StringIO.new("")) - @app.call(env) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/reloader.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/reloader.rb deleted file mode 100644 index b17d8c0..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/reloader.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'thread' - -module Rack - # Rack::Reloader checks on every request, but at most every +secs+ - # seconds, if a file loaded changed, and reloads it, logging to - # rack.errors. - # - # It is recommended you use ShowExceptions to catch SyntaxErrors etc. - - class Reloader - def initialize(app, secs=10) - @app = app - @secs = secs # reload every @secs seconds max - @last = Time.now - end - - def call(env) - if Time.now > @last + @secs - Thread.exclusive { - reload!(env['rack.errors']) - @last = Time.now - } - end - - @app.call(env) - end - - def reload!(stderr=$stderr) - need_reload = $LOADED_FEATURES.find_all { |loaded| - begin - if loaded =~ /\A[.\/]/ # absolute filename or 1.9 - abs = loaded - else - abs = $LOAD_PATH.map { |path| ::File.join(path, loaded) }. - find { |file| ::File.exist? file } - end - - if abs - ::File.mtime(abs) > @last - @secs rescue false - else - false - end - end - } - - need_reload.each { |l| - $LOADED_FEATURES.delete l - } - - need_reload.each { |to_load| - begin - if require to_load - stderr.puts "#{self.class}: reloaded `#{to_load}'" - end - rescue LoadError, SyntaxError => e - raise e # Possibly ShowExceptions - end - } - - stderr.flush - need_reload - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/request.rb deleted file mode 100644 index d77fa26..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/request.rb +++ /dev/null @@ -1,241 +0,0 @@ -require 'rack/utils' - -module Rack - # Rack::Request provides a convenient interface to a Rack - # environment. It is stateless, the environment +env+ passed to the - # constructor will be directly modified. - # - # req = Rack::Request.new(env) - # req.post? - # req.params["data"] - # - # The environment hash passed will store a reference to the Request object - # instantiated so that it will only instantiate if an instance of the Request - # object doesn't already exist. - - class Request - # The environment of the request. - attr_reader :env - - def self.new(env) - if self == Rack::Request - env["rack.request"] ||= super - else - super - end - end - - def initialize(env) - @env = env - end - - def body; @env["rack.input"] end - def scheme; @env["rack.url_scheme"] end - def script_name; @env["SCRIPT_NAME"].to_s end - def path_info; @env["PATH_INFO"].to_s end - def port; @env["SERVER_PORT"].to_i end - def request_method; @env["REQUEST_METHOD"] end - def query_string; @env["QUERY_STRING"].to_s end - def content_length; @env['CONTENT_LENGTH'] end - def content_type; @env['CONTENT_TYPE'] end - - # The media type (type/subtype) portion of the CONTENT_TYPE header - # without any media type parameters. e.g., when CONTENT_TYPE is - # "text/plain;charset=utf-8", the media-type is "text/plain". - # - # For more information on the use of media types in HTTP, see: - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 - def media_type - content_type && content_type.split(/\s*[;,]\s*/, 2)[0].downcase - end - - # The media type parameters provided in CONTENT_TYPE as a Hash, or - # an empty Hash if no CONTENT_TYPE or media-type parameters were - # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8", - # this method responds with the following Hash: - # { 'charset' => 'utf-8' } - def media_type_params - return {} if content_type.nil? - content_type.split(/\s*[;,]\s*/)[1..-1]. - collect { |s| s.split('=', 2) }. - inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash } - end - - # The character set of the request body if a "charset" media type - # parameter was given, or nil if no "charset" was specified. Note - # that, per RFC2616, text/* media types that specify no explicit - # charset are to be considered ISO-8859-1. - def content_charset - media_type_params['charset'] - end - - def host - # Remove port number. - (@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '') - end - - def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end - def path_info=(s); @env["PATH_INFO"] = s.to_s end - - def get?; request_method == "GET" end - def post?; request_method == "POST" end - def put?; request_method == "PUT" end - def delete?; request_method == "DELETE" end - def head?; request_method == "HEAD" end - - # The set of form-data media-types. Requests that do not indicate - # one of the media types presents in this list will not be eligible - # for form-data / param parsing. - FORM_DATA_MEDIA_TYPES = [ - nil, - 'application/x-www-form-urlencoded', - 'multipart/form-data' - ] - - # Determine whether the request body contains form-data by checking - # the request media_type against registered form-data media-types: - # "application/x-www-form-urlencoded" and "multipart/form-data". The - # list of form-data media types can be modified through the - # +FORM_DATA_MEDIA_TYPES+ array. - def form_data? - FORM_DATA_MEDIA_TYPES.include?(media_type) - end - - # Returns the data recieved in the query string. - def GET - if @env["rack.request.query_string"] == query_string - @env["rack.request.query_hash"] - else - @env["rack.request.query_string"] = query_string - @env["rack.request.query_hash"] = - Utils.parse_nested_query(query_string) - end - end - - # Returns the data recieved in the request body. - # - # This method support both application/x-www-form-urlencoded and - # multipart/form-data. - def POST - if @env["rack.request.form_input"].eql? @env["rack.input"] - @env["rack.request.form_hash"] - elsif form_data? - @env["rack.request.form_input"] = @env["rack.input"] - unless @env["rack.request.form_hash"] = - Utils::Multipart.parse_multipart(env) - form_vars = @env["rack.input"].read - - # Fix for Safari Ajax postings that always append \0 - form_vars.sub!(/\0\z/, '') - - @env["rack.request.form_vars"] = form_vars - @env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars) - - begin - @env["rack.input"].rewind if @env["rack.input"].respond_to?(:rewind) - rescue Errno::ESPIPE - # Handles exceptions raised by input streams that cannot be rewound - # such as when using plain CGI under Apache - end - end - @env["rack.request.form_hash"] - else - {} - end - end - - # The union of GET and POST data. - def params - self.put? ? self.GET : self.GET.update(self.POST) - rescue EOFError => e - self.GET - end - - # shortcut for request.params[key] - def [](key) - params[key.to_s] - end - - # shortcut for request.params[key] = value - def []=(key, value) - params[key.to_s] = value - end - - # like Hash#values_at - def values_at(*keys) - keys.map{|key| params[key] } - end - - # the referer of the client or '/' - def referer - @env['HTTP_REFERER'] || '/' - end - alias referrer referer - - - def cookies - return {} unless @env["HTTP_COOKIE"] - - if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] - @env["rack.request.cookie_hash"] - else - @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] - # According to RFC 2109: - # If multiple cookies satisfy the criteria above, they are ordered in - # the Cookie header such that those with more specific Path attributes - # precede those with less specific. Ordering with respect to other - # attributes (e.g., Domain) is unspecified. - @env["rack.request.cookie_hash"] = - Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)| - h[k] = Array === v ? v.first : v - h - } - end - end - - def xhr? - @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" - end - - # Tries to return a remake of the original request URL as a string. - def url - url = scheme + "://" - url << host - - if scheme == "https" && port != 443 || - scheme == "http" && port != 80 - url << ":#{port}" - end - - url << fullpath - - url - end - - def fullpath - path = script_name + path_info - path << "?" << query_string unless query_string.empty? - path - end - - def accept_encoding - @env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part| - m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick - - if m - [m[1], (m[2] || 1.0).to_f] - else - raise "Invalid value for Accept-Encoding: #{part.inspect}" - end - end - end - - def ip - if addr = @env['HTTP_X_FORWARDED_FOR'] - addr.split(',').last.strip - else - @env['REMOTE_ADDR'] - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb deleted file mode 100644 index caf60d5..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb +++ /dev/null @@ -1,179 +0,0 @@ -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::Response provides a convenient interface to create a Rack - # response. - # - # It allows setting of headers and cookies, and provides useful - # defaults (a OK response containing HTML). - # - # You can use Response#write to iteratively generate your response, - # but note that this is buffered by Rack::Response until you call - # +finish+. +finish+ however can take a block inside which calls to - # +write+ are syncronous with the Rack response. - # - # Your application's +call+ should end returning Response#finish. - - class Response - attr_accessor :length - - def initialize(body=[], status=200, header={}, &block) - @status = status - @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}. - merge(header)) - - @writer = lambda { |x| @body << x } - @block = nil - @length = 0 - - @body = [] - - if body.respond_to? :to_str - write body.to_str - elsif body.respond_to?(:each) - body.each { |part| - write part.to_s - } - else - raise TypeError, "stringable or iterable required" - end - - yield self if block_given? - end - - attr_reader :header - attr_accessor :status, :body - - def [](key) - header[key] - end - - def []=(key, value) - header[key] = value - end - - def set_cookie(key, value) - case value - when Hash - domain = "; domain=" + value[:domain] if value[:domain] - path = "; path=" + value[:path] if value[:path] - # According to RFC 2109, we need dashes here. - # N.B.: cgi.rb uses spaces... - expires = "; expires=" + value[:expires].clone.gmtime. - strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires] - secure = "; secure" if value[:secure] - httponly = "; HttpOnly" if value[:httponly] - value = value[:value] - end - value = [value] unless Array === value - cookie = Utils.escape(key) + "=" + - value.map { |v| Utils.escape v }.join("&") + - "#{domain}#{path}#{expires}#{secure}#{httponly}" - - case self["Set-Cookie"] - when Array - self["Set-Cookie"] << cookie - when String - self["Set-Cookie"] = [self["Set-Cookie"], cookie] - when nil - self["Set-Cookie"] = cookie - end - end - - def delete_cookie(key, value={}) - unless Array === self["Set-Cookie"] - self["Set-Cookie"] = [self["Set-Cookie"]].compact - end - - self["Set-Cookie"].reject! { |cookie| - cookie =~ /\A#{Utils.escape(key)}=/ - } - - set_cookie(key, - {:value => '', :path => nil, :domain => nil, - :expires => Time.at(0) }.merge(value)) - end - - - def finish(&block) - @block = block - - if [204, 304].include?(status.to_i) - header.delete "Content-Type" - [status.to_i, header.to_hash, []] - else - [status.to_i, header.to_hash, self] - end - end - alias to_a finish # For *response - - def each(&callback) - @body.each(&callback) - @writer = callback - @block.call(self) if @block - end - - # Append to body and update Content-Length. - # - # NOTE: Do not mix #write and direct #body access! - # - def write(str) - s = str.to_s - @length += s.size - @writer.call s - - header["Content-Length"] = @length.to_s - str - end - - def close - body.close if body.respond_to?(:close) - end - - def empty? - @block == nil && @body.empty? - end - - alias headers header - - module Helpers - def invalid?; @status < 100 || @status >= 600; end - - def informational?; @status >= 100 && @status < 200; end - def successful?; @status >= 200 && @status < 300; end - def redirection?; @status >= 300 && @status < 400; end - def client_error?; @status >= 400 && @status < 500; end - def server_error?; @status >= 500 && @status < 600; end - - def ok?; @status == 200; end - def forbidden?; @status == 403; end - def not_found?; @status == 404; end - - def redirect?; [301, 302, 303, 307].include? @status; end - def empty?; [201, 204, 304].include? @status; end - - # Headers - attr_reader :headers, :original_headers - - def include?(header) - !!headers[header] - end - - def content_type - headers["Content-Type"] - end - - def content_length - cl = headers["Content-Length"] - cl ? cl.to_i : cl - end - - def location - headers["Location"] - end - end - - include Helpers - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/abstract/id.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/abstract/id.rb deleted file mode 100644 index 218144c..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/abstract/id.rb +++ /dev/null @@ -1,142 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net -# bugrep: Andreas Zehnder - -require 'time' -require 'rack/request' -require 'rack/response' - -module Rack - - module Session - - module Abstract - - # ID sets up a basic framework for implementing an id based sessioning - # service. Cookies sent to the client for maintaining sessions will only - # contain an id reference. Only #get_session and #set_session are - # required to be overwritten. - # - # All parameters are optional. - # * :key determines the name of the cookie, by default it is - # 'rack.session' - # * :path, :domain, :expire_after, :secure, and :httponly set the related - # cookie options as by Rack::Response#add_cookie - # * :defer will not set a cookie in the response. - # * :renew (implementation dependent) will prompt the generation of a new - # session id, and migration of data to be referenced at the new id. If - # :defer is set, it will be overridden and the cookie will be set. - # * :sidbits sets the number of bits in length that a generated session - # id will be. - # - # These options can be set on a per request basis, at the location of - # env['rack.session.options']. Additionally the id of the session can be - # found within the options hash at the key :id. It is highly not - # recommended to change its value. - # - # Is Rack::Utils::Context compatible. - - class ID - DEFAULT_OPTIONS = { - :path => '/', - :domain => nil, - :expire_after => nil, - :secure => false, - :httponly => true, - :defer => false, - :renew => false, - :sidbits => 128 - } - - attr_reader :key, :default_options - def initialize(app, options={}) - @app = app - @key = options[:key] || "rack.session" - @default_options = self.class::DEFAULT_OPTIONS.merge(options) - end - - def call(env) - context(env) - end - - def context(env, app=@app) - load_session(env) - status, headers, body = app.call(env) - commit_session(env, status, headers, body) - end - - private - - # Generate a new session id using Ruby #rand. The size of the - # session id is controlled by the :sidbits option. - # Monkey patch this to use custom methods for session id generation. - - def generate_sid - "%0#{@default_options[:sidbits] / 4}x" % - rand(2**@default_options[:sidbits] - 1) - end - - # Extracts the session id from provided cookies and passes it and the - # environment to #get_session. It then sets the resulting session into - # 'rack.session', and places options and session metadata into - # 'rack.session.options'. - - def load_session(env) - request = Rack::Request.new(env) - session_id = request.cookies[@key] - - begin - session_id, session = get_session(env, session_id) - env['rack.session'] = session - rescue - env['rack.session'] = Hash.new - end - - env['rack.session.options'] = @default_options. - merge(:id => session_id) - end - - # Acquires the session from the environment and the session id from - # the session options and passes them to #set_session. If successful - # and the :defer option is not true, a cookie will be added to the - # response with the session's id. - - def commit_session(env, status, headers, body) - session = env['rack.session'] - options = env['rack.session.options'] - session_id = options[:id] - - if not session_id = set_session(env, session_id, session, options) - env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.") - [status, headers, body] - elsif options[:defer] and not options[:renew] - env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE - [status, headers, body] - else - cookie = Hash.new - cookie[:value] = session_id - cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? - response = Rack::Response.new(body, status, headers) - response.set_cookie(@key, cookie.merge(options)) - response.to_a - end - end - - # All thread safety and session retrival proceedures should occur here. - # Should return [session_id, session]. - # If nil is provided as the session id, generation of a new valid id - # should occur within. - - def get_session(env, sid) - raise '#get_session not implemented.' - end - - # All thread safety and session storage proceedures should occur here. - # Should return true or false dependant on whether or not the session - # was saved or not. - def set_session(env, sid, session, options) - raise '#set_session not implemented.' - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/cookie.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/cookie.rb deleted file mode 100644 index eace9bd..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/cookie.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'openssl' -require 'rack/request' -require 'rack/response' - -module Rack - - module Session - - # Rack::Session::Cookie provides simple cookie based session management. - # The session is a Ruby Hash stored as base64 encoded marshalled data - # set to :key (default: rack.session). - # When the secret key is set, cookie data is checked for data integrity. - # - # Example: - # - # use Rack::Session::Cookie, :key => 'rack.session', - # :domain => 'foo.com', - # :path => '/', - # :expire_after => 2592000, - # :secret => 'change_me' - # - # All parameters are optional. - - class Cookie - - def initialize(app, options={}) - @app = app - @key = options[:key] || "rack.session" - @secret = options[:secret] - @default_options = {:domain => nil, - :path => "/", - :expire_after => nil}.merge(options) - end - - def call(env) - load_session(env) - status, headers, body = @app.call(env) - commit_session(env, status, headers, body) - end - - private - - def load_session(env) - request = Rack::Request.new(env) - session_data = request.cookies[@key] - - if @secret && session_data - session_data, digest = session_data.split("--") - session_data = nil unless digest == generate_hmac(session_data) - end - - begin - session_data = session_data.unpack("m*").first - session_data = Marshal.load(session_data) - env["rack.session"] = session_data - rescue - env["rack.session"] = Hash.new - end - - env["rack.session.options"] = @default_options.dup - end - - def commit_session(env, status, headers, body) - session_data = Marshal.dump(env["rack.session"]) - session_data = [session_data].pack("m*") - - if @secret - session_data = "#{session_data}--#{generate_hmac(session_data)}" - end - - if session_data.size > (4096 - @key.size) - env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.") - [status, headers, body] - else - options = env["rack.session.options"] - cookie = Hash.new - cookie[:value] = session_data - cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? - response = Rack::Response.new(body, status, headers) - response.set_cookie(@key, cookie.merge(options)) - response.to_a - end - end - - def generate_hmac(data) - OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data) - end - - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/memcache.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/memcache.rb deleted file mode 100644 index 4a65cbf..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/memcache.rb +++ /dev/null @@ -1,109 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net - -require 'rack/session/abstract/id' -require 'memcache' - -module Rack - module Session - # Rack::Session::Memcache provides simple cookie based session management. - # Session data is stored in memcached. The corresponding session key is - # maintained in the cookie. - # You may treat Session::Memcache as you would Session::Pool with the - # following caveats. - # - # * Setting :expire_after to 0 would note to the Memcache server to hang - # onto the session data until it would drop it according to it's own - # specifications. However, the cookie sent to the client would expire - # immediately. - # - # Note that memcache does drop data before it may be listed to expire. For - # a full description of behaviour, please see memcache's documentation. - - class Memcache < Abstract::ID - attr_reader :mutex, :pool - DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \ - :namespace => 'rack:session', - :memcache_server => 'localhost:11211' - - def initialize(app, options={}) - super - - @mutex = Mutex.new - @pool = MemCache. - new @default_options[:memcache_server], @default_options - raise 'No memcache servers' unless @pool.servers.any?{|s|s.alive?} - end - - def generate_sid - loop do - sid = super - break sid unless @pool.get(sid, true) - end - end - - def get_session(env, sid) - session = @pool.get(sid) if sid - @mutex.lock if env['rack.multithread'] - unless sid and session - env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? - session = {} - sid = generate_sid - ret = @pool.add sid, session - raise "Session collision on '#{sid.inspect}'" unless /^STORED/ =~ ret - end - session.instance_variable_set('@old', {}.merge(session)) - return [sid, session] - rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted - warn "#{self} is unable to find server." - warn $!.inspect - return [ nil, {} ] - ensure - @mutex.unlock if env['rack.multithread'] - end - - def set_session(env, session_id, new_session, options) - expiry = options[:expire_after] - expiry = expiry.nil? ? 0 : expiry + 1 - - @mutex.lock if env['rack.multithread'] - session = @pool.get(session_id) || {} - if options[:renew] or options[:drop] - @pool.delete session_id - return false if options[:drop] - session_id = generate_sid - @pool.add session_id, 0 # so we don't worry about cache miss on #set - end - old_session = new_session.instance_variable_get('@old') || {} - session = merge_sessions session_id, old_session, new_session, session - @pool.set session_id, session, expiry - return session_id - rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted - warn "#{self} is unable to find server." - warn $!.inspect - return false - ensure - @mutex.unlock if env['rack.multithread'] - end - - private - - def merge_sessions sid, old, new, cur=nil - cur ||= {} - unless Hash === old and Hash === new - warn 'Bad old or new sessions provided.' - return cur - end - - delete = old.keys - new.keys - warn "//@#{sid}: delete #{delete*','}" if $VERBOSE and not delete.empty? - delete.each{|k| cur.delete k } - - update = new.keys.select{|k| new[k] != old[k] } - warn "//@#{sid}: update #{update*','}" if $VERBOSE and not update.empty? - update.each{|k| cur[k] = new[k] } - - cur - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/pool.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/pool.rb deleted file mode 100644 index f6f8740..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/session/pool.rb +++ /dev/null @@ -1,100 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net -# THANKS: -# apeiros, for session id generation, expiry setup, and threadiness -# sergio, threadiness and bugreps - -require 'rack/session/abstract/id' -require 'thread' - -module Rack - module Session - # Rack::Session::Pool provides simple cookie based session management. - # Session data is stored in a hash held by @pool. - # In the context of a multithreaded environment, sessions being - # committed to the pool is done in a merging manner. - # - # The :drop option is available in rack.session.options if you with to - # explicitly remove the session from the session cache. - # - # Example: - # myapp = MyRackApp.new - # sessioned = Rack::Session::Pool.new(myapp, - # :domain => 'foo.com', - # :expire_after => 2592000 - # ) - # Rack::Handler::WEBrick.run sessioned - - class Pool < Abstract::ID - attr_reader :mutex, :pool - DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false - - def initialize(app, options={}) - super - @pool = Hash.new - @mutex = Mutex.new - end - - def generate_sid - loop do - sid = super - break sid unless @pool.key? sid - end - end - - def get_session(env, sid) - session = @pool[sid] if sid - @mutex.lock if env['rack.multithread'] - unless sid and session - env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? - session = {} - sid = generate_sid - @pool.store sid, session - end - session.instance_variable_set('@old', {}.merge(session)) - return [sid, session] - ensure - @mutex.unlock if env['rack.multithread'] - end - - def set_session(env, session_id, new_session, options) - @mutex.lock if env['rack.multithread'] - session = @pool[session_id] - if options[:renew] or options[:drop] - @pool.delete session_id - return false if options[:drop] - session_id = generate_sid - @pool.store session_id, 0 - end - old_session = new_session.instance_variable_get('@old') || {} - session = merge_sessions session_id, old_session, new_session, session - @pool.store session_id, session - return session_id - rescue - warn "#{new_session.inspect} has been lost." - warn $!.inspect - ensure - @mutex.unlock if env['rack.multithread'] - end - - private - - def merge_sessions sid, old, new, cur=nil - cur ||= {} - unless Hash === old and Hash === new - warn 'Bad old or new sessions provided.' - return cur - end - - delete = old.keys - new.keys - warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty? - delete.each{|k| cur.delete k } - - update = new.keys.select{|k| new[k] != old[k] } - warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty? - update.each{|k| cur[k] = new[k] } - - cur - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/showexceptions.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/showexceptions.rb deleted file mode 100644 index 697bc41..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/showexceptions.rb +++ /dev/null @@ -1,349 +0,0 @@ -require 'ostruct' -require 'erb' -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::ShowExceptions catches all exceptions raised from the app it - # wraps. It shows a useful backtrace with the sourcefile and - # clickable context, the whole Rack environment and the request - # data. - # - # Be careful when you use this on public-facing sites as it could - # reveal information helpful to attackers. - - class ShowExceptions - CONTEXT = 7 - - def initialize(app) - @app = app - @template = ERB.new(TEMPLATE) - end - - def call(env) - @app.call(env) - rescue StandardError, LoadError, SyntaxError => e - backtrace = pretty(env, e) - [500, - {"Content-Type" => "text/html", - "Content-Length" => backtrace.join.size.to_s}, - backtrace] - end - - def pretty(env, exception) - req = Rack::Request.new(env) - path = (req.script_name + req.path_info).squeeze("/") - - frames = exception.backtrace.map { |line| - frame = OpenStruct.new - if line =~ /(.*?):(\d+)(:in `(.*)')?/ - frame.filename = $1 - frame.lineno = $2.to_i - frame.function = $4 - - begin - lineno = frame.lineno-1 - lines = ::File.readlines(frame.filename) - frame.pre_context_lineno = [lineno-CONTEXT, 0].max - frame.pre_context = lines[frame.pre_context_lineno...lineno] - frame.context_line = lines[lineno].chomp - frame.post_context_lineno = [lineno+CONTEXT, lines.size].min - frame.post_context = lines[lineno+1..frame.post_context_lineno] - rescue - end - - frame - else - nil - end - }.compact - - env["rack.errors"].puts "#{exception.class}: #{exception.message}" - env["rack.errors"].puts exception.backtrace.map { |l| "\t" + l } - env["rack.errors"].flush - - [@template.result(binding)] - end - - def h(obj) # :nodoc: - case obj - when String - Utils.escape_html(obj) - else - Utils.escape_html(obj.inspect) - end - end - - # :stopdoc: - -# adapted from Django -# Copyright (c) 2005, the Lawrence Journal-World -# Used under the modified BSD license: -# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 -TEMPLATE = <<'HTML' - - - - - - <%=h exception.class %> at <%=h path %> - - - - - -
-

<%=h exception.class %> at <%=h path %>

-

<%=h exception.message %>

- - - - - - -
Ruby<%=h frames.first.filename %>: in <%=h frames.first.function %>, line <%=h frames.first.lineno %>
Web<%=h req.request_method %> <%=h(req.host + path)%>
- -

Jump to:

- -
- -
-

Traceback (innermost first)

-
    -<% frames.each { |frame| %> -
  • - <%=h frame.filename %>: in <%=h frame.function %> - - <% if frame.context_line %> -
    - <% if frame.pre_context %> -
      - <% frame.pre_context.each { |line| %> -
    1. <%=h line %>
    2. - <% } %> -
    - <% end %> - -
      -
    1. <%=h frame.context_line %>...
    - - <% if frame.post_context %> -
      - <% frame.post_context.each { |line| %> -
    1. <%=h line %>
    2. - <% } %> -
    - <% end %> -
    - <% end %> -
  • -<% } %> -
-
- -
-

Request information

- -

GET

- <% unless req.GET.empty? %> - - - - - - - - - <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val.inspect %>
- <% else %> -

No GET data.

- <% end %> - -

POST

- <% unless req.POST.empty? %> - - - - - - - - - <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val.inspect %>
- <% else %> -

No POST data.

- <% end %> - - - - <% unless req.cookies.empty? %> - - - - - - - - - <% req.cookies.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val.inspect %>
- <% else %> -

No cookie data.

- <% end %> - -

Rack ENV

- - - - - - - - - <% env.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val %>
- -
- -
-

- You're seeing this error because you use Rack::ShowExceptions. -

-
- - - -HTML - - # :startdoc: - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb deleted file mode 100644 index 28258c7..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'erb' -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::ShowStatus catches all empty responses the app it wraps and - # replaces them with a site explaining the error. - # - # Additional details can be put into rack.showstatus.detail - # and will be shown as HTML. If such details exist, the error page - # is always rendered, even if the reply was not empty. - - class ShowStatus - def initialize(app) - @app = app - @template = ERB.new(TEMPLATE) - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - empty = headers['Content-Length'].to_i <= 0 - - # client or server error, or explicit message - if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"] - req = Rack::Request.new(env) - message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s - detail = env["rack.showstatus.detail"] || message - body = @template.result(binding) - size = Rack::Utils.bytesize(body) - [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]] - else - [status, headers, body] - end - end - - def h(obj) # :nodoc: - case obj - when String - Utils.escape_html(obj) - else - Utils.escape_html(obj.inspect) - end - end - - # :stopdoc: - -# adapted from Django -# Copyright (c) 2005, the Lawrence Journal-World -# Used under the modified BSD license: -# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 -TEMPLATE = <<'HTML' - - - - - <%=h message %> at <%=h req.script_name + req.path_info %> - - - - -
-

<%=h message %> (<%= status.to_i %>)

- - - - - - - - - -
Request Method:<%=h req.request_method %>
Request URL:<%=h req.url %>
-
-
-

<%= detail %>

-
- -
-

- You're seeing this error because you use Rack::ShowStatus. -

-
- - -HTML - - # :startdoc: - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/static.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/static.rb deleted file mode 100644 index 168e8f8..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/static.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Rack - - # The Rack::Static middleware intercepts requests for static files - # (javascript files, images, stylesheets, etc) based on the url prefixes - # passed in the options, and serves them using a Rack::File object. This - # allows a Rack stack to serve both static and dynamic content. - # - # Examples: - # use Rack::Static, :urls => ["/media"] - # will serve all requests beginning with /media from the "media" folder - # located in the current directory (ie media/*). - # - # use Rack::Static, :urls => ["/css", "/images"], :root => "public" - # will serve all requests beginning with /css or /images from the folder - # "public" in the current directory (ie public/css/* and public/images/*) - - class Static - - def initialize(app, options={}) - @app = app - @urls = options[:urls] || ["/favicon.ico"] - root = options[:root] || Dir.pwd - @file_server = Rack::File.new(root) - end - - def call(env) - path = env["PATH_INFO"] - can_serve = @urls.any? { |url| path.index(url) == 0 } - - if can_serve - @file_server.call(env) - else - @app.call(env) - end - end - - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb deleted file mode 100644 index 0ff32df..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Rack - # Rack::URLMap takes a hash mapping urls or paths to apps, and - # dispatches accordingly. Support for HTTP/1.1 host names exists if - # the URLs start with http:// or https://. - # - # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part - # relevant for dispatch is in the SCRIPT_NAME, and the rest in the - # PATH_INFO. This should be taken care of when you need to - # reconstruct the URL in order to create links. - # - # URLMap dispatches in such a way that the longest paths are tried - # first, since they are most specific. - - class URLMap - def initialize(map = {}) - remap(map) - end - - def remap(map) - @mapping = map.map { |location, app| - if location =~ %r{\Ahttps?://(.*?)(/.*)} - host, location = $1, $2 - else - host = nil - end - - unless location[0] == ?/ - raise ArgumentError, "paths need to start with /" - end - location = location.chomp('/') - - [host, location, app] - }.sort_by { |(h, l, a)| [-l.size, h.to_s.size] } # Longest path first - end - - def call(env) - path = env["PATH_INFO"].to_s.squeeze("/") - script_name = env['SCRIPT_NAME'] - hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT') - @mapping.each { |host, location, app| - next unless (hHost == host || sName == host \ - || (host.nil? && (hHost == sName || hHost == sName+':'+sPort))) - next unless location == path[0, location.size] - next unless path[location.size] == nil || path[location.size] == ?/ - - return app.call( - env.merge( - 'SCRIPT_NAME' => (script_name + location), - 'PATH_INFO' => path[location.size..-1])) - } - [404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]] - end - end -end - diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb deleted file mode 100644 index 0a61bce..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb +++ /dev/null @@ -1,392 +0,0 @@ -require 'set' -require 'tempfile' - -module Rack - # Rack::Utils contains a grab-bag of useful methods for writing web - # applications adopted from all kinds of Ruby libraries. - - module Utils - # Performs URI escaping so that you can construct proper - # query strings faster. Use this rather than the cgi.rb - # version since it's faster. (Stolen from Camping). - def escape(s) - s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) { - '%'+$1.unpack('H2'*$1.size).join('%').upcase - }.tr(' ', '+') - end - module_function :escape - - # Unescapes a URI escaped string. (Stolen from Camping). - def unescape(s) - s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ - [$1.delete('%')].pack('H*') - } - end - module_function :unescape - - # Stolen from Mongrel, with some small modifications: - # Parses a query string by breaking it up at the '&' - # and ';' characters. You can also use this to parse - # cookies by changing the characters used in the second - # parameter (which defaults to '&;'). - def parse_query(qs, d = '&;') - params = {} - - (qs || '').split(/[#{d}] */n).each do |p| - k, v = unescape(p).split('=', 2) - - if cur = params[k] - if cur.class == Array - params[k] << v - else - params[k] = [cur, v] - end - else - params[k] = v - end - end - - return params - end - module_function :parse_query - - def parse_nested_query(qs, d = '&;') - params = {} - - (qs || '').split(/[#{d}] */n).each do |p| - k, v = unescape(p).split('=', 2) - normalize_params(params, k, v) - end - - return params - end - module_function :parse_nested_query - - def normalize_params(params, name, v = nil) - name =~ %r([\[\]]*([^\[\]]+)\]*) - k = $1 || '' - after = $' || '' - - return if k.empty? - - if after == "" - params[k] = v - elsif after == "[]" - params[k] ||= [] - raise TypeError unless params[k].is_a?(Array) - params[k] << v - elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) - child_key = $1 - params[k] ||= [] - raise TypeError unless params[k].is_a?(Array) - if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) - normalize_params(params[k].last, child_key, v) - else - params[k] << normalize_params({}, child_key, v) - end - else - params[k] ||= {} - params[k] = normalize_params(params[k], after, v) - end - - return params - end - module_function :normalize_params - - def build_query(params) - params.map { |k, v| - if v.class == Array - build_query(v.map { |x| [k, x] }) - else - escape(k) + "=" + escape(v) - end - }.join("&") - end - module_function :build_query - - # Escape ampersands, brackets and quotes to their HTML/XML entities. - def escape_html(string) - string.to_s.gsub("&", "&"). - gsub("<", "<"). - gsub(">", ">"). - gsub("'", "'"). - gsub('"', """) - end - module_function :escape_html - - def select_best_encoding(available_encodings, accept_encoding) - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html - - expanded_accept_encoding = - accept_encoding.map { |m, q| - if m == "*" - (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] } - else - [[m, q]] - end - }.inject([]) { |mem, list| - mem + list - } - - encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m } - - unless encoding_candidates.include?("identity") - encoding_candidates.push("identity") - end - - expanded_accept_encoding.find_all { |m, q| - q == 0.0 - }.each { |m, _| - encoding_candidates.delete(m) - } - - return (encoding_candidates & available_encodings)[0] - end - module_function :select_best_encoding - - # Return the bytesize of String; uses String#length under Ruby 1.8 and - # String#bytesize under 1.9. - if ''.respond_to?(:bytesize) - def bytesize(string) - string.bytesize - end - else - def bytesize(string) - string.size - end - end - module_function :bytesize - - # Context allows the use of a compatible middleware at different points - # in a request handling stack. A compatible middleware must define - # #context which should take the arguments env and app. The first of which - # would be the request environment. The second of which would be the rack - # application that the request would be forwarded to. - class Context - attr_reader :for, :app - - def initialize(app_f, app_r) - raise 'running context does not respond to #context' unless app_f.respond_to? :context - @for, @app = app_f, app_r - end - - def call(env) - @for.context(env, @app) - end - - def recontext(app) - self.class.new(@for, app) - end - - def context(env, app=@app) - recontext(app).call(env) - end - end - - # A case-insensitive Hash that preserves the original case of a - # header when set. - class HeaderHash < Hash - def initialize(hash={}) - @names = {} - hash.each { |k, v| self[k] = v } - end - - def to_hash - inject({}) do |hash, (k,v)| - if v.respond_to? :to_ary - hash[k] = v.to_ary.join("\n") - else - hash[k] = v - end - hash - end - end - - def [](k) - super @names[k.downcase] - end - - def []=(k, v) - delete k - @names[k.downcase] = k - super k, v - end - - def delete(k) - super @names.delete(k.downcase) - end - - def include?(k) - @names.has_key? k.downcase - end - - alias_method :has_key?, :include? - alias_method :member?, :include? - alias_method :key?, :include? - - def merge!(other) - other.each { |k, v| self[k] = v } - self - end - - def merge(other) - hash = dup - hash.merge! other - end - end - - # Every standard HTTP code mapped to the appropriate message. - # Stolen from Mongrel. - HTTP_STATUS_CODES = { - 100 => 'Continue', - 101 => 'Switching Protocols', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Large', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported' - } - - # Responses with HTTP status codes that should not have an entity body - STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304) - - # A multipart form data parser, adapted from IOWA. - # - # Usually, Rack::Request#POST takes care of calling this. - - module Multipart - EOL = "\r\n" - - def self.parse_multipart(env) - unless env['CONTENT_TYPE'] =~ - %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n - nil - else - boundary = "--#{$1}" - - params = {} - buf = "" - content_length = env['CONTENT_LENGTH'].to_i - input = env['rack.input'] - - boundary_size = boundary.size + EOL.size - bufsize = 16384 - - content_length -= boundary_size - - status = input.read(boundary_size) - raise EOFError, "bad content body" unless status == boundary + EOL - - rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n - - loop { - head = nil - body = '' - filename = content_type = name = nil - - until head && buf =~ rx - if !head && i = buf.index("\r\n\r\n") - head = buf.slice!(0, i+2) # First \r\n - buf.slice!(0, 2) # Second \r\n - - filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1] - content_type = head[/Content-Type: (.*)\r\n/ni, 1] - name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1] - - if filename - body = Tempfile.new("RackMultipart") - body.binmode if body.respond_to?(:binmode) - end - - next - end - - # Save the read body part. - if head && (boundary_size+4 < buf.size) - body << buf.slice!(0, buf.size - (boundary_size+4)) - end - - c = input.read(bufsize < content_length ? bufsize : content_length) - raise EOFError, "bad content body" if c.nil? || c.empty? - buf << c - content_length -= c.size - end - - # Save the rest. - if i = buf.index(rx) - body << buf.slice!(0, i) - buf.slice!(0, boundary_size+2) - - content_length = -1 if $1 == "--" - end - - if filename == "" - # filename is blank which means no file has been selected - data = nil - elsif filename - body.rewind - - # Take the basename of the upload's original filename. - # This handles the full Windows paths given by Internet Explorer - # (and perhaps other broken user agents) without affecting - # those which give the lone filename. - filename =~ /^(?:.*[:\\\/])?(.*)/m - filename = $1 - - data = {:filename => filename, :type => content_type, - :name => name, :tempfile => body, :head => head} - else - data = body - end - - Utils.normalize_params(params, name, data) unless data.nil? - - break if buf.empty? || content_length == -1 - } - - begin - input.rewind if input.respond_to?(:rewind) - rescue Errno::ESPIPE - # Handles exceptions raised by input streams that cannot be rewound - # such as when using plain CGI under Apache - end - - params - end - end - end - end -end diff --git a/actionpack/test/controller/request/multipart_params_parsing_test.rb b/actionpack/test/controller/request/multipart_params_parsing_test.rb index a7b7e53..81cbd2d 100644 --- a/actionpack/test/controller/request/multipart_params_parsing_test.rb +++ b/actionpack/test/controller/request/multipart_params_parsing_test.rb @@ -96,7 +96,7 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest # Ruby CGI doesn't handle multipart/mixed for us. files = params['files'] - assert_kind_of String, files + assert_kind_of Tempfile, files files.force_encoding('ASCII-8BIT') if files.respond_to?(:force_encoding) assert_equal 19756, files.size end -- 1.6.4.2 From 61a14569379974564a98b229ab595dfec18d2059 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 25 Apr 2009 14:05:58 -0500 Subject: [PATCH 025/779] Remove RewindableInput middleware since all input MUST be rewindable according to a recent change in the Rack 1.0 SPEC --- actionpack/lib/action_controller/middlewares.rb | 1 - .../lib/action_controller/rewindable_input.rb | 19 ------ .../request/multipart_params_parsing_test.rb | 61 -------------------- .../request/url_encoded_params_parsing_test.rb | 38 ------------ 4 files changed, 0 insertions(+), 119 deletions(-) delete mode 100644 actionpack/lib/action_controller/rewindable_input.rb diff --git a/actionpack/lib/action_controller/middlewares.rb b/actionpack/lib/action_controller/middlewares.rb index 371cf6d..dda25fc 100644 --- a/actionpack/lib/action_controller/middlewares.rb +++ b/actionpack/lib/action_controller/middlewares.rb @@ -7,7 +7,6 @@ use "ActionController::Failsafe" use lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options } -use "ActionController::RewindableInput" use "ActionController::ParamsParser" use "Rack::MethodOverride" use "Rack::Head" diff --git a/actionpack/lib/action_controller/rewindable_input.rb b/actionpack/lib/action_controller/rewindable_input.rb deleted file mode 100644 index 525417f..0000000 --- a/actionpack/lib/action_controller/rewindable_input.rb +++ /dev/null @@ -1,19 +0,0 @@ -module ActionController - class RewindableInput - def initialize(app) - @app = app - end - - def call(env) - begin - env['rack.input'].rewind - rescue NoMethodError, Errno::ESPIPE - # Handles exceptions raised by input streams that cannot be rewound - # such as when using plain CGI under Apache - env['rack.input'] = StringIO.new(env['rack.input'].read) - end - - @app.call(env) - end - end -end diff --git a/actionpack/test/controller/request/multipart_params_parsing_test.rb b/actionpack/test/controller/request/multipart_params_parsing_test.rb index 81cbd2d..cc81a87 100644 --- a/actionpack/test/controller/request/multipart_params_parsing_test.rb +++ b/actionpack/test/controller/request/multipart_params_parsing_test.rb @@ -133,46 +133,6 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest end end - # The lint wrapper is used in integration tests - # instead of a normal StringIO class - InputWrapper = Rack::Lint::InputWrapper - - test "parses unwindable stream" do - InputWrapper.any_instance.stubs(:rewind).raises(Errno::ESPIPE) - params = parse_multipart('large_text_file') - assert_equal %w(file foo), params.keys.sort - assert_equal 'bar', params['foo'] - end - - test "uploads and reads file with unwindable input" do - InputWrapper.any_instance.stubs(:rewind).raises(Errno::ESPIPE) - - with_test_routing do - post '/read', :uploaded_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain") - assert_equal "File: Hello", response.body - end - end - - test "passes through rack middleware and uploads file" do - with_muck_middleware do - with_test_routing do - post '/read', :uploaded_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain") - assert_equal "File: Hello", response.body - end - end - end - - test "passes through rack middleware and uploads file with unwindable input" do - InputWrapper.any_instance.stubs(:rewind).raises(Errno::ESPIPE) - - with_muck_middleware do - with_test_routing do - post '/read', :uploaded_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain") - assert_equal "File: Hello", response.body - end - end - end - private def fixture(name) File.open(File.join(FIXTURE_PATH, name), 'rb') do |file| @@ -199,25 +159,4 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest yield end end - - class MuckMiddleware - def initialize(app) - @app = app - end - - def call(env) - env['rack.input'].read - env['rack.input'].rewind - @app.call(env) - end - end - - def with_muck_middleware - original_middleware = ActionController::Dispatcher.middleware - middleware = original_middleware.dup - middleware.insert_after ActionController::RewindableInput, MuckMiddleware - ActionController::Dispatcher.middleware = middleware - yield - ActionController::Dispatcher.middleware = original_middleware - end end diff --git a/actionpack/test/controller/request/url_encoded_params_parsing_test.rb b/actionpack/test/controller/request/url_encoded_params_parsing_test.rb index 36fbbe5..7167cda 100644 --- a/actionpack/test/controller/request/url_encoded_params_parsing_test.rb +++ b/actionpack/test/controller/request/url_encoded_params_parsing_test.rb @@ -126,45 +126,7 @@ class UrlEncodedParamsParsingTest < ActionController::IntegrationTest assert_parses expected, query end - test "passes through rack middleware and parses params" do - with_muck_middleware do - assert_parses({ "a" => { "b" => "c" } }, "a[b]=c") - end - end - - # The lint wrapper is used in integration tests - # instead of a normal StringIO class - InputWrapper = Rack::Lint::InputWrapper - - test "passes through rack middleware and parses params with unwindable input" do - InputWrapper.any_instance.stubs(:rewind).raises(Errno::ESPIPE) - with_muck_middleware do - assert_parses({ "a" => { "b" => "c" } }, "a[b]=c") - end - end - private - class MuckMiddleware - def initialize(app) - @app = app - end - - def call(env) - env['rack.input'].read - env['rack.input'].rewind - @app.call(env) - end - end - - def with_muck_middleware - original_middleware = ActionController::Dispatcher.middleware - middleware = original_middleware.dup - middleware.insert_after ActionController::RewindableInput, MuckMiddleware - ActionController::Dispatcher.middleware = middleware - yield - ActionController::Dispatcher.middleware = original_middleware - end - def with_test_routing with_routing do |set| set.draw do |map| -- 1.6.4.2 From cb9a1f17f05908768a380dce01679adb8910e638 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sat, 25 Apr 2009 12:47:51 -0700 Subject: [PATCH 026/779] Updated 2-3-stable to Rack 1.0 --- ci/geminstaller.yml | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml index 33f1e81..387e370 100644 --- a/ci/geminstaller.yml +++ b/ci/geminstaller.yml @@ -14,7 +14,7 @@ gems: - name: pg version: >= 0.7.9.2008.10.13 - name: rack - version: '~> 0.9.0' + version: '~> 1.0.0' - name: rake version: >= 0.8.1 - name: sqlite-ruby -- 1.6.4.2 From 5e57e2fa58dc4e180bbdc9ca037d0239c42f20f0 Mon Sep 17 00:00:00 2001 From: Hongli Lai (Phusion) Date: Sun, 26 Apr 2009 11:21:15 -0500 Subject: [PATCH 027/779] Remove reference to Rack::RewindableInput, which has been removed a while ago. Signed-off-by: Joshua Peek --- railties/lib/initializer.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index a04405a..e84c2dc 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -565,7 +565,7 @@ Run `rake gems:install` to install the missing gems. Rails::Rack::Metal.metal_paths += plugin_loader.engine_metal_paths configuration.middleware.insert_before( - :"ActionController::RewindableInput", + :"ActionController::ParamsParser", Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?) end -- 1.6.4.2 From 2633108e1feba908f41f64b58afa1532e89cbd30 Mon Sep 17 00:00:00 2001 From: Hongli Lai (Phusion) Date: Sun, 26 Apr 2009 11:22:44 -0500 Subject: [PATCH 028/779] Fix environment variable testing code in failsafe.rb. Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/failsafe.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/failsafe.rb b/actionpack/lib/action_controller/failsafe.rb index 644eb72..36d0ab5 100644 --- a/actionpack/lib/action_controller/failsafe.rb +++ b/actionpack/lib/action_controller/failsafe.rb @@ -11,7 +11,7 @@ module ActionController @app.call(env) rescue Exception => exception # Reraise exception in test environment - if defined?(Rails) && Rails.test? + if defined?(Rails) && Rails.env.test? raise exception else failsafe_response(exception) -- 1.6.4.2 From 4b68debb1c4d3d272b237049c88d01b1eceb58f0 Mon Sep 17 00:00:00 2001 From: Stephen Bannasch Date: Sun, 15 Mar 2009 06:32:29 -0400 Subject: [PATCH 029/779] add JRuby-JDOM backend for XmlMini Signed-off-by: Jeremy Kemper --- activesupport/lib/active_support/xml_mini/jdom.rb | 162 +++++++++++++++++++++ activesupport/test/xml_mini/jdom_engine_test.rb | 153 +++++++++++++++++++ 2 files changed, 315 insertions(+), 0 deletions(-) create mode 100644 activesupport/lib/active_support/xml_mini/jdom.rb create mode 100644 activesupport/test/xml_mini/jdom_engine_test.rb diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb new file mode 100644 index 0000000..d795d55 --- /dev/null +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -0,0 +1,162 @@ +raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFORM =~ /java/ + +require 'jruby' +include Java + +import javax.xml.parsers.DocumentBuilder unless defined? DocumentBuilder +import javax.xml.parsers.DocumentBuilderFactory unless defined? DocumentBuilderFactory +import java.io.StringReader unless defined? StringReader +import org.xml.sax.InputSource unless defined? InputSource +import org.xml.sax.Attributes unless defined? Attributes +import org.w3c.dom.Node unless defined? Node + +# = XmlMini JRuby JDOM implementation +module ActiveSupport + module XmlMini_JDOM #:nodoc: + extend self + + CONTENT_KEY = '__content__'.freeze + + NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE + DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE + PROCESSING_INSTRUCTION_NODE TEXT_NODE} + + 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? + {} + else + @dbf = DocumentBuilderFactory.new_instance + xml_string_reader = StringReader.new(string) + xml_input_source = InputSource.new(xml_string_reader) + doc = @dbf.new_document_builder.parse(xml_input_source) + merge_element!({}, doc.document_element) + end + end + + private + + # Convert an XML element and merge into the hash + # + # hash:: + # Hash to merge the converted element into. + # element:: + # XML element to merge into hash + def merge_element!(hash, element) + merge!(hash, element.tag_name, collapse(element)) + end + + # Actually converts an XML document element into a data structure. + # + # element:: + # The document element to be collapsed. + def collapse(element) + hash = get_attributes(element) + + child_nodes = element.child_nodes + if child_nodes.length > 0 + for i in 0...child_nodes.length + child = child_nodes.item(i) + merge_element!(hash, child) unless child.node_type == Node.TEXT_NODE + end + merge_texts!(hash, element) unless empty_content?(element) + hash + else + merge_texts!(hash, element) + end + end + + # Merge all the texts of an element into the hash + # + # hash:: + # Hash to add the converted emement to. + # element:: + # XML element whose texts are to me merged into the hash + def merge_texts!(hash, element) + text_children = texts(element) + if text_children.join.empty? + hash + else + # must use value to prevent double-escaping + merge!(hash, CONTENT_KEY, text_children.join) + end + end + + # Adds a new key/value pair to an existing Hash. If the key to be added + # already exists and the existing value associated with key is not + # an Array, it will be wrapped in an Array. Then the new value is + # appended to that Array. + # + # hash:: + # Hash to add key/value pair to. + # key:: + # Key to be added. + # value:: + # Value to be associated with key. + def merge!(hash, key, value) + if hash.has_key?(key) + if hash[key].instance_of?(Array) + hash[key] << value + else + hash[key] = [hash[key], value] + end + elsif value.instance_of?(Array) + hash[key] = [value] + else + hash[key] = value + end + hash + end + + # Converts the attributes array of an XML element into a hash. + # Returns an empty Hash if node has no attributes. + # + # element:: + # XML element to extract attributes from. + def get_attributes(element) + attribute_hash = {} + attributes = element.attributes + for i in 0...attributes.length + attribute_hash[attributes.item(i).name] = attributes.item(i).value + end + attribute_hash + end + + # Determines if a document element has text content + # + # element:: + # XML element to be checked. + def texts(element) + texts = [] + child_nodes = element.child_nodes + for i in 0...child_nodes.length + item = child_nodes.item(i) + if item.node_type == Node.TEXT_NODE + texts << item.get_data + end + end + texts + end + + # Determines if a document element has text content + # + # element:: + # XML element to be checked. + def empty_content?(element) + text = '' + child_nodes = element.child_nodes + for i in 0...child_nodes.length + item = child_nodes.item(i) + if item.node_type == Node.TEXT_NODE + text << item.get_data.strip + end + end + text.strip.length == 0 + end + end +end diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb new file mode 100644 index 0000000..b745228 --- /dev/null +++ b/activesupport/test/xml_mini/jdom_engine_test.rb @@ -0,0 +1,153 @@ +require 'abstract_unit' +require 'active_support/xml_mini' + +if RUBY_PLATFORM =~ /java/ + +class JDOMEngineTest < Test::Unit::TestCase + include ActiveSupport + + def setup + @default_backend = XmlMini.backend + XmlMini.backend = 'JDOM' + end + + def teardown + XmlMini.backend = @default_backend + end + + # def test_file_from_xml + # hash = Hash.from_xml(<<-eoxml) + # + # + # + # + # eoxml + # assert hash.has_key?('blog') + # assert hash['blog'].has_key?('logo') + # + # file = hash['blog']['logo'] + # assert_equal 'logo.png', file.original_filename + # assert_equal 'image/png', file.content_type + # end + + def test_exception_thrown_on_expansion_attack + assert_raise NativeException do + attack_xml = <<-EOT + + + + + + + + + ]> + + &a; + + EOT + Hash.from_xml(attack_xml) + end + end + + def test_setting_JDOM_as_backend + XmlMini.backend = 'JDOM' + assert_equal XmlMini_JDOM, XmlMini.backend + end + + def test_blank_returns_empty_hash + assert_equal({}, XmlMini.parse(nil)) + assert_equal({}, XmlMini.parse('')) + end + + def test_array_type_makes_an_array + assert_equal_rexml(<<-eoxml) + + + a post + another post + + + eoxml + end + + def test_one_node_document_as_hash + assert_equal_rexml(<<-eoxml) + + eoxml + end + + def test_one_node_with_attributes_document_as_hash + assert_equal_rexml(<<-eoxml) + + eoxml + end + + def test_products_node_with_book_node_as_hash + assert_equal_rexml(<<-eoxml) + + + + eoxml + end + + def test_products_node_with_two_book_nodes_as_hash + assert_equal_rexml(<<-eoxml) + + + + + eoxml + end + + def test_single_node_with_content_as_hash + assert_equal_rexml(<<-eoxml) + + hello world + + eoxml + end + + def test_children_with_children + assert_equal_rexml(<<-eoxml) + + + + + + eoxml + end + + def test_children_with_text + assert_equal_rexml(<<-eoxml) + + + hello everyone + + + eoxml + end + + def test_children_with_non_adjacent_text + assert_equal_rexml(<<-eoxml) + + good + + hello everyone + + morning + + eoxml + end + + private + def assert_equal_rexml(xml) + hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } + assert_equal(hash, XmlMini.parse(xml)) + end +end + +else + # don't run these test because we aren't running in JRuby +end -- 1.6.4.2 From 6e3bede928d95cbf5fe70ab2283adce28da3401d Mon Sep 17 00:00:00 2001 From: David Dollar Date: Wed, 29 Apr 2009 01:22:54 -0400 Subject: [PATCH 030/779] Attempt to deal with more cases of gems with native components. This commit adds a rudimentary check for 'unbuilt' gems, so that we can abort the application load if there are any gems that have native components that have not yet been built. The rake task gems:build has now only builds 'unbuilt' gems as a result. The rake task gems:build:force has been added to deal with cases of incomplete builds, or any case where you need to force the build of all of your gems. Changes the gems:build task to get its gem list by parsing directory entries in vendor/gems, which sidesteps the chicken/egg issues involved with having a gem unpacked into vendor/gems without before its native bits are compiled. [#2266 state:committed] Signed-off-by: Jeremy Kemper --- railties/lib/initializer.rb | 21 ++++++++++ railties/lib/rails/gem_dependency.rb | 21 ++++++++-- railties/lib/tasks/gems.rake | 18 ++++++++- railties/test/gem_dependency_test.rb | 21 ++++++++++ .../vendor/gems/dummy-gem-i-1.0.0/.specification | 41 ++++++++++++++++++++ .../gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb | 1 + .../vendor/gems/dummy-gem-j-1.0.0/.specification | 41 ++++++++++++++++++++ .../gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb | 1 + 8 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification create mode 100644 railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile create mode 100644 railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb create mode 100644 railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification create mode 100644 railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index e84c2dc..4d34b82 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -159,6 +159,8 @@ module Rails add_support_load_paths + check_for_unbuilt_gems + load_gems load_plugins @@ -306,6 +308,25 @@ module Rails end end + def check_for_unbuilt_gems + unbuilt_gems = @configuration.gems.select(&:frozen?).reject(&:built?) + if unbuilt_gems.size > 0 + # don't print if the gems:build rake tasks are being run + unless $gems_build_rake_task + abort <<-end_error +The following gems have native components that need to be built + #{unbuilt_gems.map { |gem| "#{gem.name} #{gem.requirement}" } * "\n "} + +You're running: + ruby #{Gem.ruby_version} at #{Gem.ruby} + rubygems #{Gem::RubyGemsVersion} at #{Gem.path * ', '} + +Run `rake gems:build` to build the unbuilt gems. + end_error + end + end + end + def check_gem_dependencies unloaded_gems = @configuration.gems.reject { |g| g.loaded? } if unloaded_gems.size > 0 diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb index 3062a77..ee3d0d8 100644 --- a/railties/lib/rails/gem_dependency.rb +++ b/railties/lib/rails/gem_dependency.rb @@ -29,6 +29,15 @@ module Rails end end + def self.from_directory_name(directory_name) + directory_name_parts = File.basename(directory_name).split('-') + name = directory_name_parts[0..-2].join('-') + version = directory_name_parts.last + self.new(name, :version => version) + rescue ArgumentError => e + raise "Unable to determine gem name and version from '#{directory_name}'" + end + def initialize(name, options = {}) require 'rubygems' unless Object.const_defined?(:Gem) @@ -101,8 +110,12 @@ module Rails end def built? - # TODO: If Rubygems ever gives us a way to detect this, we should use it - false + return false unless frozen? + specification.extensions.each do |ext| + makefile = File.join(unpacked_gem_directory, File.dirname(ext), 'Makefile') + return false unless File.exists?(makefile) + end + true end def framework_gem? @@ -155,9 +168,9 @@ module Rails specification && File.exists?(unpacked_gem_directory) end - def build + def build(options={}) require 'rails/gem_builder' - unless built? + if options[:force] || !built? return unless File.exists?(unpacked_specification_filename) spec = YAML::load_file(unpacked_specification_filename) Rails::GemBuilder.new(spec, unpacked_gem_directory).build_extensions diff --git a/railties/lib/tasks/gems.rake b/railties/lib/tasks/gems.rake index ed07bf2..efadb1d 100644 --- a/railties/lib/tasks/gems.rake +++ b/railties/lib/tasks/gems.rake @@ -20,8 +20,16 @@ namespace :gems do desc "Build any native extensions for unpacked gems" task :build do $gems_build_rake_task = true - Rake::Task['gems:unpack'].invoke - current_gems.each &:build + frozen_gems.each &:build + end + + namespace :build do + desc "Force the build of all gems" + task :force do + $gems_build_rake_task = true + Rake::Task['gems:unpack'].invoke + current_gems.each { |gem| gem.build(:force => true) } + end end desc "Installs all required gems." @@ -53,6 +61,12 @@ def current_gems gems end +def frozen_gems + Dir[File.join(RAILS_ROOT, 'vendor', 'gems', '*-*')].map do |gem_dir| + Rails::GemDependency.from_directory_name(gem_dir) + end +end + def print_gem_status(gem, indent=1) code = case when gem.framework_gem? then 'R' diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb index 189ad02..9fd5e93 100644 --- a/railties/test/gem_dependency_test.rb +++ b/railties/test/gem_dependency_test.rb @@ -144,4 +144,25 @@ class GemDependencyTest < Test::Unit::TestCase end end + def test_gem_from_directory_name + dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1') + assert_equal 'dummy-gem', dummy_gem.name + assert_equal '= 1.1', dummy_gem.version_requirements.to_s + end + + def test_gem_from_invalid_directory_name + assert_raises RuntimeError do + dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem') + end + assert_raises RuntimeError do + dummy_gem = Rails::GemDependency.from_directory_name('dummy') + end + end + + def test_gem_determines_build_status + assert_equal true, Rails::GemDependency.new("dummy-gem-a").built? + assert_equal true, Rails::GemDependency.new("dummy-gem-i").built? + assert_equal false, Rails::GemDependency.new("dummy-gem-j").built? + end + end diff --git a/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification b/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification new file mode 100644 index 0000000..50b4969 --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-i-1.0.0/.specification @@ -0,0 +1,41 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-i +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-i + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +extensions: +- ext/dummy-gem-i/extconf.rb +files: +- lib +- lib/dummy-gem-i.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem G diff --git a/railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile b/railties/test/vendor/gems/dummy-gem-i-1.0.0/ext/dummy-gem-i/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb b/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb new file mode 100644 index 0000000..2f9a376 --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-i-1.0.0/lib/dummy-gem-i.rb @@ -0,0 +1 @@ +DUMMY_GEM_I_VERSION="1.0.0" diff --git a/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification b/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification new file mode 100644 index 0000000..2c45654 --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-j-1.0.0/.specification @@ -0,0 +1,41 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-j +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-j + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +extensions: +- ext/dummy-gem-j/extconf.rb +files: +- lib +- lib/dummy-gem-j.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem G diff --git a/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb b/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb new file mode 100644 index 0000000..8ecd363 --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-j-1.0.0/lib/dummy-gem-j.rb @@ -0,0 +1 @@ +DUMMY_GEM_J_VERSION="1.0.0" -- 1.6.4.2 From 00eee49e1e5d1ce394c639fc5542f993bae49ed7 Mon Sep 17 00:00:00 2001 From: David Dollar Date: Wed, 29 Apr 2009 10:04:17 -0400 Subject: [PATCH 031/779] Additional tests for the gem subsystem * test_gem_ignores_development_dependencies * test_gem_guards_against_duplicate_unpacks * test_gem_does_not_unpack_framework_gems [#2236 state:committed] Signed-off-by: Jeremy Kemper --- railties/test/gem_dependency_test.rb | 21 ++++++++ .../vendor/gems/dummy-gem-h-1.0.0/.specification | 29 ++++++++++++ .../gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb | 1 + .../vendor/gems/dummy-gem-k-1.0.0/.specification | 49 ++++++++++++++++++++ .../gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb | 1 + 5 files changed, 101 insertions(+), 0 deletions(-) create mode 100644 railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification create mode 100644 railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb create mode 100644 railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification create mode 100644 railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb index 9fd5e93..3853eb5 100644 --- a/railties/test/gem_dependency_test.rb +++ b/railties/test/gem_dependency_test.rb @@ -144,6 +144,27 @@ class GemDependencyTest < Test::Unit::TestCase end end + def test_gem_ignores_development_dependencies + dummy_gem = Rails::GemDependency.new "dummy-gem-k" + dummy_gem.add_load_paths + dummy_gem.load + assert_equal 1, dummy_gem.dependencies.size + end + + def test_gem_guards_against_duplicate_unpacks + dummy_gem = Rails::GemDependency.new "dummy-gem-a" + dummy_gem.stubs(:frozen?).returns(true) + dummy_gem.expects(:unpack_base).never + dummy_gem.unpack + end + + def test_gem_does_not_unpack_framework_gems + dummy_gem = Rails::GemDependency.new "dummy-gem-a" + dummy_gem.stubs(:framework_gem?).returns(true) + dummy_gem.expects(:unpack_base).never + dummy_gem.unpack + end + def test_gem_from_directory_name dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1') assert_equal 'dummy-gem', dummy_gem.name diff --git a/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification b/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification new file mode 100644 index 0000000..b3f7930 --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-h-1.0.0/.specification @@ -0,0 +1,29 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-h +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +files: +- lib +- lib/dummy-gem-h.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem H diff --git a/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb b/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb new file mode 100644 index 0000000..0f91234 --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-h-1.0.0/lib/dummy-gem-h.rb @@ -0,0 +1 @@ +DUMMY_GEM_H_VERSION="1.0.0" diff --git a/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification b/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification new file mode 100644 index 0000000..20edd0f --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-k-1.0.0/.specification @@ -0,0 +1,49 @@ +--- !ruby/object:Gem::Specification +name: dummy-gem-k +version: !ruby/object:Gem::Version + version: 1.3.0 +platform: ruby +authors: +- "Nobody" +date: 2008-10-03 00:00:00 -04:00 +dependencies: +- !ruby/object:Gem::Dependency + name: dummy-gem-k + type: :runtime + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +- !ruby/object:Gem::Dependency + name: dummy-gem-h + type: :development + version_requirement: + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.0.0 + version: +files: +- lib +- lib/dummy-gem-k.rb +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: "0" + version: +requirements: [] +specification_version: 2 +summary: Dummy Gem I diff --git a/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb b/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb new file mode 100644 index 0000000..97fb1d6 --- /dev/null +++ b/railties/test/vendor/gems/dummy-gem-k-1.0.0/lib/dummy-gem-k.rb @@ -0,0 +1 @@ +DUMMY_GEM_K_VERSION="1.0.0" -- 1.6.4.2 From 7c4b325e0a3a73d7910b8cad062d1154d47e225b Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 30 Apr 2009 16:34:00 -0700 Subject: [PATCH 032/779] Fix render :json => nil [#2589 state:resolved] --- actionpack/test/controller/render_test.rb | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-) diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index a529315..9930824 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -193,6 +193,10 @@ class TestController < ActionController::Base render :inline => "<%= controller_name %>" end + def render_json_nil + render :json => nil + end + def render_json_hello_world render :json => {:hello => 'world'}.to_json end @@ -886,6 +890,12 @@ class RenderTest < ActionController::TestCase assert_equal "The secret is in the sauce\n", @response.body end + def test_render_json_nil + get :render_json_nil + assert_equal 'null', @response.body + assert_equal 'application/json', @response.content_type + end + def test_render_json get :render_json_hello_world assert_equal '{"hello": "world"}', @response.body -- 1.6.4.2 From d1d1894c2f38b7b116a2f756fb879ef0e68ade3f Mon Sep 17 00:00:00 2001 From: John F. Douthat Date: Tue, 21 Apr 2009 13:35:54 -0500 Subject: [PATCH 033/779] Fix action-cached exception responses. Methods raising ActiveRecord::RecordNotFound were returning 404 on first request and 200 OK with blank body on subsequent requests. [#2533 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/action_controller/caching/actions.rb | 10 +++++- actionpack/test/controller/caching_test.rb | 35 ++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index 87b5029..21ee9bd 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -61,7 +61,9 @@ 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(cache_filter, filter_options) + around_filter(filter_options) do |controller, action| + cache_filter.filter(controller, action) + end end end @@ -83,6 +85,12 @@ module ActionController #:nodoc: @options = options end + def filter(controller, action) + should_continue = before(controller) + action.call 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]) diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 86dafd9..e5eee15 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -1,5 +1,6 @@ require 'fileutils' require 'abstract_unit' +require 'active_record_unit' CACHE_DIR = 'test_cache' # Don't change '/../temp/' cavalierly or you might hose something you don't want hosed @@ -152,6 +153,7 @@ class ActionCachingTestController < ActionController::Base caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" } caches_action :with_layout caches_action :layout_false, :layout => false + caches_action :record_not_found, :four_oh_four, :simple_runtime_error layout 'talk_from_action.erb' @@ -174,6 +176,18 @@ class ActionCachingTestController < ActionController::Base render :text => @cache_this, :layout => true end + def record_not_found + raise ActiveRecord::RecordNotFound, "oops!" + end + + def four_oh_four + render :text => "404'd!", :status => 404 + end + + def simple_runtime_error + raise "oops!" + end + alias_method :show, :index alias_method :edit, :index alias_method :destroy, :index @@ -456,6 +470,27 @@ class ActionCacheTest < ActionController::TestCase assert_response :success end + def test_record_not_found_returns_404_for_multiple_requests + get :record_not_found + assert_response 404 + get :record_not_found + assert_response 404 + end + + def test_four_oh_four_returns_404_for_multiple_requests + get :four_oh_four + assert_response 404 + get :four_oh_four + assert_response 404 + end + + def test_simple_runtime_error_returns_500_for_multiple_requests + get :simple_runtime_error + assert_response 500 + get :simple_runtime_error + assert_response 500 + end + private def content_to_cache assigns(:cache_this) -- 1.6.4.2 From a5ed7eede609790fba9ae11db1e2fd46b9806aa3 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 30 Apr 2009 16:49:34 -0700 Subject: [PATCH 034/779] Missed commit for 7c4b325e0a3a73d7910b8cad062d1154d47e225b --- actionpack/lib/action_controller/base.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 0facf70..8669d1d 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -950,7 +950,8 @@ module ActionController #:nodoc: response.content_type ||= Mime::JS render_for_text(js, options[:status]) - elsif json = options[:json] + elsif options.include?(:json) + json = options[:json] json = json.to_json unless json.is_a?(String) json = "#{options[:callback]}(#{json})" unless options[:callback].blank? response.content_type ||= Mime::JSON -- 1.6.4.2 From 628b4ad679b1971427a20461e8c2332d492e4655 Mon Sep 17 00:00:00 2001 From: Alexander Podgorbunsky Date: Thu, 26 Mar 2009 15:00:12 +0300 Subject: [PATCH 035/779] Default scope :order should be overridden by named scopes. [#2346 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/named_scope.rb | 4 ++-- activerecord/test/cases/method_scoping_test.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 1f3ef30..3df7089 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -114,7 +114,7 @@ module ActiveRecord end end - delegate :scopes, :with_scope, :to => :proxy_scope + delegate :scopes, :with_scope, :scoped_methods, :to => :proxy_scope def initialize(proxy_scope, options, &block) options ||= {} @@ -178,7 +178,7 @@ module ActiveRecord else with_scope({:find => proxy_options, :create => proxy_options[:conditions].is_a?(Hash) ? proxy_options[:conditions] : {}}, :reverse_merge) do method = :new if method == :build - if current_scoped_methods_when_defined + if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined) with_scope current_scoped_methods_when_defined do proxy_scope.send(method, *args, &block) end diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 3c34cde..9f23064 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -628,9 +628,9 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal expected, received end - def test_named_scope - expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary } - received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.salary } + def test_named_scope_overwrites_default + expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name } + received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name } assert_equal expected, received end -- 1.6.4.2 From 93c557828e1873004911acfd25d3b3903210bc40 Mon Sep 17 00:00:00 2001 From: Ruy Asan Date: Fri, 1 May 2009 13:09:29 -0700 Subject: [PATCH 036/779] Fixed bug with polymorphic has_one :as pointing to an STI record [#2594 state:committed] Signed-off-by: Jeremy Kemper --- .../associations/has_one_association.rb | 2 +- .../associations/has_one_associations_test.rb | 9 ++++++++- activerecord/test/fixtures/organizations.yml | 4 +++- activerecord/test/fixtures/sponsors.yml | 4 +++- activerecord/test/models/organization.rb | 4 ++++ activerecord/test/schema/schema.rb | 3 ++- 6 files changed, 21 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index b92cbbd..1464227 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -88,7 +88,7 @@ module ActiveRecord when @reflection.options[:as] @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " + - "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" + "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.name.to_s)}" else @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 1ddb3f4..3984945 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -2,9 +2,11 @@ require "cases/helper" require 'models/developer' require 'models/project' require 'models/company' +require 'models/sponsor' +require 'models/organization' class HasOneAssociationsTest < ActiveRecord::TestCase - fixtures :accounts, :companies, :developers, :projects, :developers_projects + fixtures :accounts, :companies, :developers, :projects, :developers_projects, :organizations, :sponsors def setup Account.destroyed_account_ids.clear @@ -306,4 +308,9 @@ class HasOneAssociationsTest < ActiveRecord::TestCase Firm.find(@firm.id, :include => :account).save! end end + + def test_polymorphic_sti + assert_equal organizations(:sponsorable), sponsors(:org_sponsor).sponsorable + assert_equal sponsors(:org_sponsor), organizations(:sponsorable).sponsor + end end diff --git a/activerecord/test/fixtures/organizations.yml b/activerecord/test/fixtures/organizations.yml index 25295bf..05d620f 100644 --- a/activerecord/test/fixtures/organizations.yml +++ b/activerecord/test/fixtures/organizations.yml @@ -2,4 +2,6 @@ nsa: name: No Such Agency discordians: name: Discordians - +sponsorable: + name: We Need Money + type: SponsorableOrganization diff --git a/activerecord/test/fixtures/sponsors.yml b/activerecord/test/fixtures/sponsors.yml index 42df895..97a7784 100644 --- a/activerecord/test/fixtures/sponsors.yml +++ b/activerecord/test/fixtures/sponsors.yml @@ -6,4 +6,6 @@ boring_club_sponsor_for_groucho: sponsorable: some_other_guy (Member) crazy_club_sponsor_for_groucho: sponsor_club: crazy_club - sponsorable: some_other_guy (Member) \ No newline at end of file + sponsorable: some_other_guy (Member) +org_sponsor: + sponsorable: sponsorable (SponsorableOrganization) \ No newline at end of file diff --git a/activerecord/test/models/organization.rb b/activerecord/test/models/organization.rb index d79d503..5d13083 100644 --- a/activerecord/test/models/organization.rb +++ b/activerecord/test/models/organization.rb @@ -1,4 +1,8 @@ class Organization < ActiveRecord::Base has_many :member_details has_many :members, :through => :member_details +end + +class SponsorableOrganization < Organization + has_one :sponsor, :as => :sponsorable end \ No newline at end of file diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 98e6d19..6918a4f 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -283,6 +283,7 @@ ActiveRecord::Schema.define do create_table :organizations, :force => true do |t| t.string :name + t.string :type end create_table :owners, :primary_key => :owner_id ,:force => true do |t| @@ -388,7 +389,7 @@ ActiveRecord::Schema.define do create_table :sponsors, :force => true do |t| t.integer :club_id t.integer :sponsorable_id - t.string :sponsorable_type + t.string :sponsorable_type end create_table :subscribers, :force => true, :id => false do |t| -- 1.6.4.2 From 17e712d3a3d3934bb1f694d449d9a76a3ac715c1 Mon Sep 17 00:00:00 2001 From: Ruy Asan Date: Fri, 1 May 2009 13:49:18 -0700 Subject: [PATCH 037/779] Added routing test for irregular ID requirements and custom member action. [#2595 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/test/controller/resources_test.rb | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index c807e71..30ab110 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -120,6 +120,14 @@ class ResourcesTest < ActionController::TestCase end end + def test_irregular_id_requirements_should_get_passed_to_member_actions + expected_options = {:controller => 'messages', :action => 'custom', :id => '1.1.1'} + + with_restful_routing(:messages, :member => {:custom => :get}, :requirements => {:id => /[0-9]\.[0-9]\.[0-9]/}) do + assert_recognizes(expected_options, :path => 'messages/1.1.1/custom', :method => :get) + end + end + def test_with_path_prefix with_restful_routing :messages, :path_prefix => '/thread/:thread_id' do assert_simply_restful_for :messages, :path_prefix => 'thread/5/', :options => { :thread_id => '5' } -- 1.6.4.2 From ebe8dd6108ab9b2ecd74bacf7fa128e9d4f28e4e Mon Sep 17 00:00:00 2001 From: codebrulee Date: Mon, 4 May 2009 09:51:35 -0700 Subject: [PATCH 038/779] Remove stray underscore from the hash conversion methods which broke backwards compatibility with Hash.from_xml Also add an all-caps test to prevent future regressions --- .../active_support/core_ext/hash/conversions.rb | 2 +- activesupport/test/core_ext/hash_ext_test.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 1043597..37d035e 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -221,7 +221,7 @@ module ActiveSupport #:nodoc: case params.class.to_s when "Hash" params.inject({}) do |h,(k,v)| - h[k.to_s.underscore.tr("-", "_")] = unrename_keys(v) + h[k.to_s.tr("-", "_")] = unrename_keys(v) h end when "Array" diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 0edac72..31519a1 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -654,6 +654,22 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal expected_blog_hash, Hash.from_xml(blog_xml) end + def test_all_caps_key_from_xml + test_xml = <<-EOT + + Lorem Ipsum + + EOT + + expected_hash = { + "ABC3XYZ" => { + "TEST" => "Lorem Ipsum" + } + } + + assert_equal expected_hash, Hash.from_xml(test_xml) + end + def test_empty_array_with_whitespace_from_xml blog_xml = <<-XML -- 1.6.4.2 From 49169f7a6ab2699c500032e6e14570512c674274 Mon Sep 17 00:00:00 2001 From: Tim Connor Date: Mon, 4 May 2009 20:12:16 -0500 Subject: [PATCH 039/779] fix problems with requires in metal choking under development reloading [#2579 state:resolved] Signed-off-by: Joshua Peek --- railties/lib/rails/rack/metal.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/rails/rack/metal.rb b/railties/lib/rails/rack/metal.rb index adc43da..8719a5c 100644 --- a/railties/lib/rails/rack/metal.rb +++ b/railties/lib/rails/rack/metal.rb @@ -26,7 +26,7 @@ module Rails load_list.map do |requested_metal| if metal = all_metals[requested_metal] - require metal + require_dependency metal requested_metal.constantize end end.compact -- 1.6.4.2 From 5ac05f15c6d8f496c4e152dbbecd8ccb12041770 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Mon, 4 May 2009 20:17:27 -0500 Subject: [PATCH 040/779] Extract ActionController::Caching::Sweeper into separate file [#1977 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/caching.rb | 2 +- .../lib/action_controller/caching/sweeper.rb | 46 ++++++++++++++++++++ .../lib/action_controller/caching/sweeping.rb | 42 ------------------ 3 files changed, 47 insertions(+), 43 deletions(-) create mode 100644 actionpack/lib/action_controller/caching/sweeper.rb diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 80d13e2..f686223 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -27,7 +27,7 @@ module ActionController #:nodoc: 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 :Sweeper, 'action_controller/caching/sweeper' autoload :Sweeping, 'action_controller/caching/sweeping' def self.included(base) #:nodoc: diff --git a/actionpack/lib/action_controller/caching/sweeper.rb b/actionpack/lib/action_controller/caching/sweeper.rb new file mode 100644 index 0000000..3d5b8d5 --- /dev/null +++ b/actionpack/lib/action_controller/caching/sweeper.rb @@ -0,0 +1,46 @@ +require 'active_record' + +module ActionController #:nodoc: + module Caching + module Sweeping + class Sweeper < ActiveRecord::Observer #:nodoc: + attr_accessor :controller + + def before(controller) + self.controller = controller + callback(:before) if controller.perform_caching + end + + def after(controller) + callback(:after) if controller.perform_caching + # Clean up, so that the controller can be collected after this request + self.controller = nil + end + + protected + # gets the action cache path for the given options. + def action_path_for(options) + ActionController::Caching::Actions::ActionCachePath.path_for(controller, options) + end + + # Retrieve instance variables set in the controller. + def assigns(key) + controller.instance_variable_get("@#{key}") + end + + private + def callback(timing) + controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}" + action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}" + + __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true) + __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true) + end + + def method_missing(method, *arguments, &block) + return if @controller.nil? + @controller.__send__(method, *arguments, &block) + end + end + end +end diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb index c1be264..9b32002 100644 --- a/actionpack/lib/action_controller/caching/sweeping.rb +++ b/actionpack/lib/action_controller/caching/sweeping.rb @@ -51,47 +51,5 @@ module ActionController #:nodoc: end end end - - if defined?(ActiveRecord) and defined?(ActiveRecord::Observer) - class Sweeper < ActiveRecord::Observer #:nodoc: - attr_accessor :controller - - def before(controller) - self.controller = controller - callback(:before) if controller.perform_caching - end - - def after(controller) - callback(:after) if controller.perform_caching - # Clean up, so that the controller can be collected after this request - self.controller = nil - end - - protected - # gets the action cache path for the given options. - def action_path_for(options) - ActionController::Caching::Actions::ActionCachePath.path_for(controller, options) - end - - # Retrieve instance variables set in the controller. - def assigns(key) - controller.instance_variable_get("@#{key}") - end - - private - def callback(timing) - controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}" - action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}" - - __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true) - __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true) - end - - def method_missing(method, *arguments, &block) - return if @controller.nil? - @controller.__send__(method, *arguments, &block) - end - end - end end end -- 1.6.4.2 From 7f1f16c01fd42701747daf9f3399dcdd300125f4 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 4 May 2009 20:24:49 -0500 Subject: [PATCH 041/779] Deprecate assert_redirect_to's partial hash matching. This will be fully removed in 3.0. --- .../assertions/response_assertions.rb | 5 ++++- actionpack/test/controller/redirect_test.rb | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb index 5976090..43e83ac 100644 --- a/actionpack/lib/action_controller/assertions/response_assertions.rb +++ b/actionpack/lib/action_controller/assertions/response_assertions.rb @@ -63,7 +63,10 @@ module ActionController # Support partial arguments for hash redirections if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash) - return true if options.all? {|(key, value)| @response.redirected_to[key] == value} + if options.all? {|(key, value)| @response.redirected_to[key] == value} + ::ActiveSupport::Deprecation.warn("Using assert_redirected_to with partial hash arguments is deprecated. Specify the full set arguments instead", caller) + return true + end end redirected_to_after_normalisation = normalize_argument_to_redirection(@response.redirected_to) diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 91e21db..c457486 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -235,7 +235,10 @@ class RedirectTest < ActionController::TestCase def test_redirect_with_partial_params get :module_redirect - assert_redirected_to :action => 'hello_world' + + assert_deprecated do + assert_redirected_to :action => 'hello_world' + end end def test_redirect_to_nil -- 1.6.4.2 From e61cceb37fa99a0eaf510e6e8138eca4b9cefcfd Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 8 May 2009 17:00:16 -0500 Subject: [PATCH 042/779] Don't stream each line of the body, just send the whole thing --- actionpack/lib/action_controller/response.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb index ccff473..971ef7b 100644 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_controller/response.rb @@ -152,7 +152,7 @@ module ActionController # :nodoc: @writer = lambda { |x| callback.call(x) } @body.call(self, self) elsif @body.is_a?(String) - @body.each_line(&callback) + yield @body else @body.each(&callback) end -- 1.6.4.2 From 6dec3c45fcf9afbd3da30afb354433fcc54155d6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 9 May 2009 18:35:31 -0400 Subject: [PATCH 043/779] ActiveSupport::OrderedHash#to_a method returns an ordered set of arrays. Matches ruby1.9's Hash#to_a. Signed-off-by: Michael Koziarski [#2629 state:committed] --- activesupport/lib/active_support/ordered_hash.rb | 4 ++++ activesupport/test/ordered_hash_test.rb | 4 ++++ 2 files changed, 8 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index fed8094..33fd2a2 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -57,6 +57,10 @@ module ActiveSupport self end + def to_a + @keys.map { |key| [ key, self[key] ] } + end + def each_key @keys.each { |key| yield key } end diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 7cd8c8a..6fccbbd 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -51,6 +51,10 @@ class OrderedHashTest < Test::Unit::TestCase assert_same @ordered_hash, @ordered_hash.to_hash end + def test_to_a + assert_equal @keys.zip(@values), @ordered_hash.to_a + end + def test_has_key assert_equal true, @ordered_hash.has_key?('blue') assert_equal true, @ordered_hash.key?('blue') -- 1.6.4.2 From 7bf9bf3dd6b4c4d78214917f0877536d222098bb Mon Sep 17 00:00:00 2001 From: John Small Date: Fri, 8 May 2009 06:47:04 +0100 Subject: [PATCH 044/779] Add configuration options for :dasherize and :camelize calls to Hash#to_xml People using ActiveResource & REST to integrate with other systems need to be able to control the default dasherize behavior of Hash.to_xml. Currently there is no test for a default value, but existing code asssumes it's true. This patch adds tests for the default value and adds mattr_accessor to ActiveSupport for :dasherize_xml and :camelize_xml. These module attributes set the defaults for :dasherize and :camelize in rename_keys inside Hash#to_xml. The tests have been changed to separate out the testing of the parameter options for :camelize and :dasherize so that we only test one thing at a time. We also test default values for :camelize_xml and :dasherize_xml. The module attribute dasherize_xml is set to true in this patch to maintain existing code. But at some point in the future it should be set to false because Hash#to_xml probably should not set underscores to dashes by default. Changed documentation on ActiveResource#to_xml to correctly describe the behaviour of Hash#to_xml. The previous documentation said that the default for :dasherize was false, in fact it was and still is true, but we now have a way to change the default. I've also added documentation for the :camelize option. Signed-off-by: Michael Koziarski --- activeresource/lib/active_resource/base.rb | 11 +++- .../active_support/core_ext/hash/conversions.rb | 15 ++++- activesupport/test/core_ext/hash_ext_test.rb | 66 ++++++++++++++++++- 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index a8c0da3..8ff7ab4 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -832,7 +832,7 @@ module ActiveResource !new? && self.class.exists?(to_param, :params => prefix_options) end - # A method to convert the the resource to an XML string. + # Converts the resource to an XML string representation. # # ==== Options # The +options+ parameter is handed off to the +to_xml+ method on each @@ -841,7 +841,14 @@ module ActiveResource # # * :indent - Set the indent level for the XML output (default is +2+). # * :dasherize - Boolean option to determine whether or not element names should - # replace underscores with dashes (default is false). + # replace underscores with dashes. Default is true. The default can be set to false + # by setting the module attribute ActiveSupport.dasherize_xml = false in an initializer. Because save + # uses this method, and there are no options on save, then you will have to set the default if you don't + # want underscores in element names to become dashes when the resource is saved. This is important when + # integrating with non-Rails applications. + # * :camelize - Boolean option to determine whether or not element names should be converted + # to camel case, e.g some_name to SomeName. Default is false. Like :dasherize you can + # change the default by setting the module attribute ActiveSupport.camelise_xml = true in an initializer. # * :skip_instruct - Toggle skipping the +instruct!+ call on the XML builder # that generates the XML declaration (default is false). # diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 37d035e..5ae43c0 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -1,6 +1,14 @@ require 'date' +require 'active_support/core_ext/module/attribute_accessors' module ActiveSupport #:nodoc: + # these accessors are here because people using ActiveResource and REST to integrate with other systems + # have to be able to control the default behavior of rename_key. dasherize_xml is set to true to emulate + # existing behavior. In a future version it should be set to false by default. + mattr_accessor :dasherize_xml + mattr_accessor :camelize_xml + self.dasherize_xml = true + self.camelize_xml = false module CoreExtensions #:nodoc: module Hash #:nodoc: module Conversions @@ -143,10 +151,11 @@ module ActiveSupport #:nodoc: end def rename_key(key, options = {}) - camelize = options.has_key?(:camelize) && options[:camelize] - dasherize = !options.has_key?(:dasherize) || options[:dasherize] + camelize = options.has_key?(:camelize) ? options[:camelize] : ActiveSupport.camelize_xml + dasherize = options.has_key?(:dasherize) ? options[:dasherize] : ActiveSupport.dasherize_xml key = key.camelize if camelize - dasherize ? key.dasherize : key + key = key.dasherize if dasherize + key end module ClassMethods diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 31519a1..c58c0ac 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -403,29 +403,87 @@ class HashToXmlTest < Test::Unit::TestCase @xml_options = { :root => :person, :skip_instruct => true, :indent => 0 } end + def test_default_values_for_rename_keys + assert_equal true,ActiveSupport.dasherize_xml + assert_equal false,ActiveSupport.camelize_xml + end + def test_one_level xml = { :name => "David", :street => "Paulina" }.to_xml(@xml_options) assert_equal "", xml.first(8) assert xml.include?(%(Paulina)) assert xml.include?(%(David)) end - + # we add :camelize => false because otherwise we'd be accidentally testing the default value for :camelize def test_one_level_dasherize_false - xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:dasherize => false)) + xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:dasherize => false,:camelize=>false)) assert_equal "", xml.first(8) assert xml.include?(%(Paulina)) assert xml.include?(%(David)) end def test_one_level_dasherize_true - xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:dasherize => true)) + xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:dasherize => true,:camelize=>false)) + assert_equal "", xml.first(8) + assert xml.include?(%(Paulina)) + assert xml.include?(%(David)) + end + + def test_one_level_dasherize_default_false + current_default = ActiveSupport.dasherize_xml + ActiveSupport.dasherize_xml = false + xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:camelize=>false)) + assert_equal "", xml.first(8) + assert xml.include?(%(Paulina)) + assert xml.include?(%(David)) + ensure + ActiveSupport.dasherize_xml = current_default + end + + def test_one_level_dasherize_default_true + current_default = ActiveSupport.dasherize_xml + ActiveSupport.dasherize_xml = true + xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:camelize=>false)) assert_equal "", xml.first(8) assert xml.include?(%(Paulina)) assert xml.include?(%(David)) + ensure + ActiveSupport.dasherize_xml = current_default end def test_one_level_camelize_true - xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:camelize => true)) + xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:camelize => true,:dasherize => false)) + assert_equal "", xml.first(8) + assert xml.include?(%(Paulina)) + assert xml.include?(%(David)) + end + + #camelize=>false is already tested above + + def test_one_level_camelize_default_false + current_default = ActiveSupport.camelize_xml + ActiveSupport.camelize_xml = false + xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:dasherize => false)) + assert_equal "", xml.first(8) + assert xml.include?(%(Paulina)) + assert xml.include?(%(David)) + ensure + ActiveSupport.camelize_xml = current_default + end + + def test_one_level_camelize_default_true + current_default = ActiveSupport.camelize_xml + ActiveSupport.camelize_xml = true + xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:dasherize => false)) + assert_equal "", xml.first(8) + assert xml.include?(%(Paulina)) + assert xml.include?(%(David)) + ensure + ActiveSupport.camelize_xml = current_default + end + + def test_one_level_camelize_true_dasherize_true + xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:dasherize => true,:camelize=>true)) assert_equal "", xml.first(8) assert xml.include?(%(Paulina)) assert xml.include?(%(David)) -- 1.6.4.2 From 88d5e3341d0a602135c40d01ca0e5d8a345a0781 Mon Sep 17 00:00:00 2001 From: Anthony Crumley Date: Mon, 4 May 2009 09:49:43 -0500 Subject: [PATCH 045/779] Fixed eager load error on find with include => [:table_name] and hash conditions like {:table_name => {:column => 'value'}} Signed-off-by: Michael Koziarski --- activerecord/lib/active_record/associations.rb | 20 ++++++++++++++++---- activerecord/test/cases/associations/eager_test.rb | 12 ++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 2115878..8491a26 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1665,17 +1665,29 @@ module ActiveRecord string.scan(/([\.a-zA-Z_]+).?\./).flatten end + def tables_in_hash(hash) + return [] if hash.blank? + tables = hash.map do |key, value| + if value.is_a?(Hash) + key.to_s + else + tables_in_string(key) if key.is_a?(String) + end + end + tables.flatten.compact + end + def conditions_tables(options) # look in both sets of conditions conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond| case cond when nil then all - when Array then all << cond.first - when Hash then all << cond.keys - else all << cond + when Array then all << tables_in_string(cond.first) + when Hash then all << tables_in_hash(cond) + else all << tables_in_string(cond) end end - tables_in_string(conditions.join(' ')) + conditions.flatten end def order_tables(options) diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 4072381..d23f86b 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -223,6 +223,18 @@ class EagerAssociationTest < ActiveRecord::TestCase end end + def test_eager_association_loading_with_belongs_to_and_conditions_hash + comments = [] + assert_nothing_raised do + comments = Comment.find(:all, :include => :post, :conditions => {:posts => {:id => 4}}, :limit => 3, :order => 'comments.id') + end + assert_equal 3, comments.length + assert_equal [5,6,7], comments.collect { |c| c.id } + assert_no_queries do + comments.first.post + end + end + def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id') assert_nothing_raised do -- 1.6.4.2 From 4051dd3412ad6b233b8bcdea17cfb829f21b2fa8 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 9 May 2009 22:21:02 -0500 Subject: [PATCH 046/779] Fix syntax error from 5ac05f15 --- .../lib/action_controller/caching/sweeper.rb | 69 ++++++++++---------- 1 files changed, 35 insertions(+), 34 deletions(-) diff --git a/actionpack/lib/action_controller/caching/sweeper.rb b/actionpack/lib/action_controller/caching/sweeper.rb index 3d5b8d5..54848d5 100644 --- a/actionpack/lib/action_controller/caching/sweeper.rb +++ b/actionpack/lib/action_controller/caching/sweeper.rb @@ -2,45 +2,46 @@ require 'active_record' module ActionController #:nodoc: module Caching - module Sweeping - class Sweeper < ActiveRecord::Observer #:nodoc: - attr_accessor :controller + module Sweeping + class Sweeper < ActiveRecord::Observer #:nodoc: + attr_accessor :controller - def before(controller) - self.controller = controller - callback(:before) if controller.perform_caching - end - - def after(controller) - callback(:after) if controller.perform_caching - # Clean up, so that the controller can be collected after this request - self.controller = nil - end - - protected - # gets the action cache path for the given options. - def action_path_for(options) - ActionController::Caching::Actions::ActionCachePath.path_for(controller, options) + def before(controller) + self.controller = controller + callback(:before) if controller.perform_caching end - # Retrieve instance variables set in the controller. - def assigns(key) - controller.instance_variable_get("@#{key}") + def after(controller) + callback(:after) if controller.perform_caching + # Clean up, so that the controller can be collected after this request + self.controller = nil end - private - def callback(timing) - controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}" - action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}" - - __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true) - __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true) - end - - def method_missing(method, *arguments, &block) - return if @controller.nil? - @controller.__send__(method, *arguments, &block) - end + protected + # gets the action cache path for the given options. + def action_path_for(options) + ActionController::Caching::Actions::ActionCachePath.path_for(controller, options) + end + + # Retrieve instance variables set in the controller. + def assigns(key) + controller.instance_variable_get("@#{key}") + end + + private + def callback(timing) + controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}" + action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}" + + __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true) + __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true) + end + + def method_missing(method, *arguments, &block) + return if @controller.nil? + @controller.__send__(method, *arguments, &block) + end + end end end end -- 1.6.4.2 From 2bcb2443a9e2140e29799229d6c63a046e149597 Mon Sep 17 00:00:00 2001 From: Douglas F Shearer Date: Thu, 7 May 2009 23:58:07 +0100 Subject: [PATCH 047/779] ActiveSupport::OrderedHash[1,2,3,4] creates an OrderedHash instead of a Hash. [#2615 state:committed] Signed-off-by: Jeremy Kemper --- activesupport/lib/active_support/ordered_hash.rb | 10 ++++++++++ activesupport/test/ordered_hash_test.rb | 6 ++++++ 2 files changed, 16 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 33fd2a2..8d1c0f5 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -10,6 +10,16 @@ module ActiveSupport @keys = [] end + def self.[](*args) + ordered_hash = new + args.each_with_index { |val,ind| + # Only every second value is a key. + next if ind % 2 != 0 + ordered_hash[val] = args[ind + 1] + } + ordered_hash + end + def initialize_copy(other) super # make a deep copy of keys diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 6fccbbd..647938d 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -162,4 +162,10 @@ class OrderedHashTest < Test::Unit::TestCase def test_inspect assert @ordered_hash.inspect.include?(@hash.inspect) end + + def test_alternate_initialization + alternate = ActiveSupport::OrderedHash[1,2,3,4] + assert_kind_of ActiveSupport::OrderedHash, alternate + assert_equal [1, 3], alternate.keys + end end -- 1.6.4.2 From 35e17850819d99d78a3bd02865652c7882201bf0 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 11 May 2009 12:01:27 -0700 Subject: [PATCH 048/779] Revert "Fixed bug with polymorphic has_one :as pointing to an STI record" [#2594 state:open] This reverts commit 93c557828e1873004911acfd25d3b3903210bc40. --- .../associations/has_one_association.rb | 2 +- .../associations/has_one_associations_test.rb | 9 +-------- activerecord/test/fixtures/organizations.yml | 4 +--- activerecord/test/fixtures/sponsors.yml | 4 +--- activerecord/test/models/organization.rb | 4 ---- activerecord/test/schema/schema.rb | 3 +-- 6 files changed, 5 insertions(+), 21 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index 1464227..b92cbbd 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -88,7 +88,7 @@ module ActiveRecord when @reflection.options[:as] @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " + - "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.name.to_s)}" + "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" else @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 3984945..1ddb3f4 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -2,11 +2,9 @@ require "cases/helper" require 'models/developer' require 'models/project' require 'models/company' -require 'models/sponsor' -require 'models/organization' class HasOneAssociationsTest < ActiveRecord::TestCase - fixtures :accounts, :companies, :developers, :projects, :developers_projects, :organizations, :sponsors + fixtures :accounts, :companies, :developers, :projects, :developers_projects def setup Account.destroyed_account_ids.clear @@ -308,9 +306,4 @@ class HasOneAssociationsTest < ActiveRecord::TestCase Firm.find(@firm.id, :include => :account).save! end end - - def test_polymorphic_sti - assert_equal organizations(:sponsorable), sponsors(:org_sponsor).sponsorable - assert_equal sponsors(:org_sponsor), organizations(:sponsorable).sponsor - end end diff --git a/activerecord/test/fixtures/organizations.yml b/activerecord/test/fixtures/organizations.yml index 05d620f..25295bf 100644 --- a/activerecord/test/fixtures/organizations.yml +++ b/activerecord/test/fixtures/organizations.yml @@ -2,6 +2,4 @@ nsa: name: No Such Agency discordians: name: Discordians -sponsorable: - name: We Need Money - type: SponsorableOrganization + diff --git a/activerecord/test/fixtures/sponsors.yml b/activerecord/test/fixtures/sponsors.yml index 97a7784..42df895 100644 --- a/activerecord/test/fixtures/sponsors.yml +++ b/activerecord/test/fixtures/sponsors.yml @@ -6,6 +6,4 @@ boring_club_sponsor_for_groucho: sponsorable: some_other_guy (Member) crazy_club_sponsor_for_groucho: sponsor_club: crazy_club - sponsorable: some_other_guy (Member) -org_sponsor: - sponsorable: sponsorable (SponsorableOrganization) \ No newline at end of file + sponsorable: some_other_guy (Member) \ No newline at end of file diff --git a/activerecord/test/models/organization.rb b/activerecord/test/models/organization.rb index 5d13083..d79d503 100644 --- a/activerecord/test/models/organization.rb +++ b/activerecord/test/models/organization.rb @@ -1,8 +1,4 @@ class Organization < ActiveRecord::Base has_many :member_details has_many :members, :through => :member_details -end - -class SponsorableOrganization < Organization - has_one :sponsor, :as => :sponsorable end \ No newline at end of file diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 6918a4f..98e6d19 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -283,7 +283,6 @@ ActiveRecord::Schema.define do create_table :organizations, :force => true do |t| t.string :name - t.string :type end create_table :owners, :primary_key => :owner_id ,:force => true do |t| @@ -389,7 +388,7 @@ ActiveRecord::Schema.define do create_table :sponsors, :force => true do |t| t.integer :club_id t.integer :sponsorable_id - t.string :sponsorable_type + t.string :sponsorable_type end create_table :subscribers, :force => true, :id => false do |t| -- 1.6.4.2 From 0380e9ca5fa872e56d0920e6255a2f20b6e01030 Mon Sep 17 00:00:00 2001 From: Peter Marklund Date: Thu, 14 May 2009 09:30:16 +0200 Subject: [PATCH 049/779] Changed ActiveRecord::Base#exists? to invoke find_initial so that it is compatible with, and doesn't lose, :include scopes (references to eager loaded tables) Signed-off-by: Michael Koziarski [#2543 state:committed] --- activerecord/lib/active_record/base.rb | 11 +++-------- activerecord/test/cases/finder_test.rb | 6 ++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 84b22a5..545ee83 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -687,14 +687,9 @@ module ActiveRecord #:nodoc: # Person.exists?(['name LIKE ?', "%#{query}%"]) # Person.exists? def exists?(id_or_conditions = {}) - connection.select_all( - construct_finder_sql( - :select => "#{quoted_table_name}.#{primary_key}", - :conditions => expand_id_conditions(id_or_conditions), - :limit => 1 - ), - "#{name} Exists" - ).size > 0 + find_initial( + :select => "#{quoted_table_name}.#{primary_key}", + :conditions => expand_id_conditions(id_or_conditions)) ? true : false end # Creates an object (or multiple objects) and saves it to the database, if validations pass. diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index d877895..25e339f 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -119,6 +119,12 @@ class FinderTest < ActiveRecord::TestCase Address.new(existing_address.street + "1", existing_address.city, existing_address.country)) end + def test_exists_with_scoped_include + Developer.with_scope(:find => { :include => :projects, :order => "projects.name" }) do + assert Developer.exists? + end + end + def test_find_by_array_of_one_id assert_kind_of(Array, Topic.find([ 1 ])) assert_equal(1, Topic.find([ 1 ]).length) -- 1.6.4.2 From f7cb7fce4cb25e608f0c2d94a4970c0c7cb7d3da Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 14 May 2009 16:47:24 -0500 Subject: [PATCH 050/779] Sweeper does not belong in Sweeping module --- .../lib/action_controller/caching/sweeper.rb | 68 ++++++++++---------- actionpack/test/controller/caching_test.rb | 4 + 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/actionpack/lib/action_controller/caching/sweeper.rb b/actionpack/lib/action_controller/caching/sweeper.rb index 54848d5..4885d8b 100644 --- a/actionpack/lib/action_controller/caching/sweeper.rb +++ b/actionpack/lib/action_controller/caching/sweeper.rb @@ -2,46 +2,44 @@ require 'active_record' module ActionController #:nodoc: module Caching - module Sweeping - class Sweeper < ActiveRecord::Observer #:nodoc: - attr_accessor :controller + class Sweeper < ActiveRecord::Observer #:nodoc: + attr_accessor :controller - def before(controller) - self.controller = controller - callback(:before) if controller.perform_caching + def before(controller) + self.controller = controller + callback(:before) if controller.perform_caching + end + + def after(controller) + callback(:after) if controller.perform_caching + # Clean up, so that the controller can be collected after this request + self.controller = nil + end + + protected + # gets the action cache path for the given options. + def action_path_for(options) + ActionController::Caching::Actions::ActionCachePath.path_for(controller, options) end - def after(controller) - callback(:after) if controller.perform_caching - # Clean up, so that the controller can be collected after this request - self.controller = nil + # Retrieve instance variables set in the controller. + def assigns(key) + controller.instance_variable_get("@#{key}") end - protected - # gets the action cache path for the given options. - def action_path_for(options) - ActionController::Caching::Actions::ActionCachePath.path_for(controller, options) - end - - # Retrieve instance variables set in the controller. - def assigns(key) - controller.instance_variable_get("@#{key}") - end - - private - def callback(timing) - controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}" - action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}" - - __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true) - __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true) - end - - def method_missing(method, *arguments, &block) - return if @controller.nil? - @controller.__send__(method, *arguments, &block) - end - end + private + def callback(timing) + controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}" + action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}" + + __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true) + __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true) + end + + def method_missing(method, *arguments, &block) + return if @controller.nil? + @controller.__send__(method, *arguments, &block) + end end end end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index e5eee15..6dcb67e 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -8,6 +8,10 @@ FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR) ActionController::Base.page_cache_directory = FILE_STORE_PATH ActionController::Base.cache_store = :file_store, FILE_STORE_PATH +# Force sweeper classes to load +ActionController::Caching::Sweeper +ActionController::Caching::Sweeping + class PageCachingTestController < ActionController::Base caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? } caches_page :found, :not_found -- 1.6.4.2 From 66ead4f148964ad7af33f5e44e79fa50a7a00f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 1 Apr 2009 12:44:56 +0200 Subject: [PATCH 051/779] Allow strings to be sent as collection to select. Signed-off-by: Michael Koziarski [#2391 state:committed] --- .../lib/action_view/helpers/form_options_helper.rb | 2 + .../test/template/form_options_helper_test.rb | 22 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 6b385ef..6adbab1 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -230,6 +230,8 @@ module ActionView # # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag. def options_for_select(container, selected = nil) + return container if String === container + container = container.to_a if Hash === container selected, disabled = extract_selected_and_disabled(selected) diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 78db879..7362440 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -80,6 +80,14 @@ class FormOptionsHelperTest < ActionView::TestCase ) end + def test_string_options_for_select + options = "" + assert_dom_equal( + options, + options_for_select(options) + ) + end + def test_array_options_for_select assert_dom_equal( "\n\n", @@ -324,6 +332,20 @@ class FormOptionsHelperTest < ActionView::TestCase ) end + def test_select_under_fields_for_with_string_and_given_prompt + @post = Post.new + options = "" + + fields_for :post, @post do |f| + concat f.select(:category, options, :prompt => 'The prompt') + end + + assert_dom_equal( + "", + output_buffer + ) + end + def test_select_with_blank @post = Post.new @post.category = "" -- 1.6.4.2 From ba92e83bcc8120eeac9f9f53b1705f48360ec070 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 May 2009 17:04:43 +0200 Subject: [PATCH 052/779] Include guides directory in the rails gem --- railties/Rakefile | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/railties/Rakefile b/railties/Rakefile index 6c0fc22..0e8cd9b 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -246,7 +246,7 @@ def copy_with_rewritten_ruby_path(src_file, dest_file) end desc 'Generate guides (for authors), use ONLY=foo to process just "foo.textile"' -task :guides do +task :generate_guides do ruby "guides/rails_guides.rb" end @@ -294,6 +294,7 @@ PKG_FILES = FileList[ 'doc/**/*', 'dispatches/**/*', 'environments/**/*', + 'guides/**/*', 'helpers/**/*', 'generators/**/*', 'html/**/*', @@ -320,7 +321,7 @@ spec = Gem::Specification.new do |s| s.rdoc_options << '--exclude' << '.' s.has_rdoc = false - s.files = PKG_FILES.to_a.delete_if {|f| f.include?('.svn')} + s.files = PKG_FILES.to_a.delete_if {|f| f =~ %r{\.svn|guides/output}} s.require_path = 'lib' s.bindir = "bin" # Use these for applications. s.executables = ["rails"] @@ -345,7 +346,7 @@ task :pgem => [:gem] do end desc "Publish the guides" -task :pguides => :guides do +task :pguides => :generate_guides do mkdir_p 'pkg' `tar -czf pkg/guides.gz guides/output` Rake::SshFilePublisher.new("web.rubyonrails.org", "/u/sites/guides.rubyonrails.org/public", "pkg", "guides.gz").upload -- 1.6.4.2 From 4cd40726eb10fbab269be53c27b304c728541dff Mon Sep 17 00:00:00 2001 From: Daniel Guettler Date: Sun, 17 May 2009 14:48:20 +0200 Subject: [PATCH 053/779] has_one :through should not create a new association when assigned nil [#698 state:resolved] Signed-off-by: Pratik Naik --- .../associations/has_one_through_association.rb | 16 ++++++++-------- .../has_one_through_associations_test.rb | 9 ++++++++- 2 files changed, 16 insertions(+), 9 deletions(-) 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 8073eba..d93c8e7 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/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 12c5987..ab6e6d2 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 -- 1.6.4.2 From d5f018eb103b63738f7f3b2374242fbdd40cfd63 Mon Sep 17 00:00:00 2001 From: Jacob Kjeldahl Date: Tue, 27 Jan 2009 15:00:18 +0100 Subject: [PATCH 054/779] Supply valid ruby-prof parameters [#1804 state:resolved] Signed-off-by: Pratik Naik --- railties/lib/commands/performance/profiler.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/commands/performance/profiler.rb b/railties/lib/commands/performance/profiler.rb index fd111ba..7df840f 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.' -- 1.6.4.2 From 14b769899cd045ba066e91d372906fe51eca319e Mon Sep 17 00:00:00 2001 From: Paulo Schneider Date: Mon, 6 Apr 2009 21:38:24 +0100 Subject: [PATCH 055/779] Fix typo in the generated routes.rb [#2433 state:resolved] Signed-off-by: Pratik Naik --- railties/configs/routes.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/configs/routes.rb b/railties/configs/routes.rb index 4f3d9d2..ea14ce1 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 -- 1.6.4.2 From f383a4aa333cd8a99003eb1bdbb27b6fdea1056c Mon Sep 17 00:00:00 2001 From: Mike Breen Date: Mon, 16 Mar 2009 07:11:16 -0400 Subject: [PATCH 056/779] Allow assert_template to take a symbol [#2011 state:resolved] Signed-off-by: Pratik Naik --- .../assertions/response_assertions.rb | 9 +++++- .../test/controller/action_pack_assertions_test.rb | 27 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb index 43e83ac..989be2d 100644 --- a/actionpack/lib/action_controller/assertions/response_assertions.rb +++ b/actionpack/lib/action_controller/assertions/response_assertions.rb @@ -85,6 +85,9 @@ module ActionController # # assert that the "new" view template was rendered # assert_template "new" # + # # assert that the "new" view template was rendered with Symbol + # assert_template :new + # # # assert that the "_customer" partial was rendered twice # assert_template :partial => '_customer', :count => 2 # @@ -94,7 +97,7 @@ module ActionController def assert_template(options = {}, message = nil) clean_backtrace do case options - when NilClass, String + when NilClass, String, Symbol rendered = @response.rendered[:template].to_s msg = build_message(message, "expecting but rendering with ", @@ -103,7 +106,7 @@ module ActionController if options.nil? @response.rendered[:template].blank? else - rendered.to_s.match(options) + rendered.to_s.match(options.to_s) end end when Hash @@ -126,6 +129,8 @@ module ActionController assert @response.rendered[:partials].empty?, "Expected no partials to be rendered" end + else + raise ArgumentError end end end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index cb7922e..6e92eff 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -11,6 +11,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 @@ -332,6 +335,30 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase assert @response.rendered[:template] assert 'hello_world', @response.rendered[:template].to_s 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 + + def test_assert_template_with_bad_argument + assert_raise(ArgumentError) { assert_template 1 } + end # check the redirection location def test_redirection_location -- 1.6.4.2 From e30016c29e4ec58352a15511afa12f0fbbd03a8c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 17 May 2009 14:44:19 -0500 Subject: [PATCH 057/779] Fix reset_session with ActiveRecord store [#2200 state:resolved] --- .../test/activerecord/active_record_store_test.rb | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb index c98892e..bde36eb 100644 --- a/actionpack/test/activerecord/active_record_store_test.rb +++ b/actionpack/test/activerecord/active_record_store_test.rb @@ -27,9 +27,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 @@ -86,7 +86,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 -- 1.6.4.2 From 43e537b9e8a8e34aaf57cec33cd91574ee5c1459 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 17 May 2009 14:45:06 -0500 Subject: [PATCH 058/779] Missed a file from the previous commit --- activerecord/lib/active_record/session_store.rb | 10 +++++++++- 1 files changed, 9 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 3cc4640..c91b943 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) || -- 1.6.4.2 From d052e9fb5868a10df33a84bf61f40a32df9e78ec Mon Sep 17 00:00:00 2001 From: rick Date: Sun, 17 May 2009 14:55:11 -0700 Subject: [PATCH 059/779] Add pluggable JSON backends with support for the JSON gem. Example: ActiveSupport::JSON.backend = "JSONGem" All internal Rails JSON encoding is now handled by ActiveSupport::JSON.encode(). Use of #to_json is not recommended, as it may clash with other libraries that overwrite it. However, you can recover Rails specific functionality if you really want to use #to_json. gem 'json' ActiveSupport::JSON.backend = "JSONGem" class ActiveRecord::Base alias to_json rails_to_json end --- actionpack/lib/action_controller/base.rb | 2 +- .../lib/action_view/helpers/prototype_helper.rb | 16 ++-- .../action_view/helpers/scriptaculous_helper.rb | 10 +- actionpack/test/controller/render_test.rb | 18 ++-- actionpack/test/template/prototype_helper_test.rb | 16 ++-- .../active_record/serializers/json_serializer.rb | 10 ++- activerecord/test/cases/json_serialization_test.rb | 112 ++++++++++---------- activeresource/lib/active_resource/base.rb | 5 +- activesupport/lib/active_support/json.rb | 33 ++++++- .../lib/active_support/json/backends/jsongem.rb | 36 ++++++ .../lib/active_support/json/backends/yaml.rb | 83 +++++++++++++++ activesupport/lib/active_support/json/decoding.rb | 82 -------------- .../lib/active_support/json/encoders/date.rb | 4 +- .../lib/active_support/json/encoders/date_time.rb | 4 +- .../lib/active_support/json/encoders/enumerable.rb | 6 +- .../active_support/json/encoders/false_class.rb | 4 +- .../lib/active_support/json/encoders/hash.rb | 8 +- .../lib/active_support/json/encoders/nil_class.rb | 4 +- .../lib/active_support/json/encoders/numeric.rb | 4 +- .../lib/active_support/json/encoders/object.rb | 4 +- .../lib/active_support/json/encoders/regexp.rb | 4 +- .../lib/active_support/json/encoders/string.rb | 4 +- .../lib/active_support/json/encoders/symbol.rb | 4 +- .../lib/active_support/json/encoders/time.rb | 4 +- .../lib/active_support/json/encoders/true_class.rb | 4 +- activesupport/lib/active_support/json/encoding.rb | 2 +- activesupport/lib/active_support/json/variable.rb | 4 +- activesupport/lib/active_support/time_with_zone.rb | 4 +- activesupport/test/core_ext/time_with_zone_test.rb | 4 +- activesupport/test/json/decoding_test.rb | 79 +++++++++----- activesupport/test/json/encoding_test.rb | 38 ++++---- 31 files changed, 371 insertions(+), 241 deletions(-) create mode 100644 activesupport/lib/active_support/json/backends/jsongem.rb create mode 100644 activesupport/lib/active_support/json/backends/yaml.rb delete mode 100644 activesupport/lib/active_support/json/decoding.rb diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 8669d1d..95cf3e7 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -952,7 +952,7 @@ module ActionController #:nodoc: elsif options.include?(:json) json = options[:json] - json = json.to_json unless json.is_a?(String) + json = ActiveSupport::JSON.encode(json) unless json.is_a?(String) json = "#{options[:callback]}(#{json})" unless options[:callback].blank? response.content_type ||= Mime::JSON render_for_text(json, options[:status]) diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 91ef72e..0502e17 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -973,7 +973,7 @@ module ActionView def loop_on_multiple_args(method, ids) record(ids.size>1 ? "#{javascript_object_for(ids)}.each(#{method})" : - "#{method}(#{ids.first.to_json})") + "#{method}(#{ActiveSupport::JSON.encode(ids.first)})") end def page @@ -997,7 +997,7 @@ module ActionView end def javascript_object_for(object) - object.respond_to?(:to_json) ? object.to_json : object.inspect + ActiveSupport::JSON.encode(object) end def arguments_for_call(arguments, block = nil) @@ -1139,7 +1139,7 @@ module ActionView class JavaScriptElementProxy < JavaScriptProxy #:nodoc: def initialize(generator, id) @id = id - super(generator, "$(#{id.to_json})") + super(generator, "$(#{ActiveSupport::JSON.encode(id)})") end # Allows access of element attributes through +attribute+. Examples: @@ -1184,10 +1184,12 @@ module ActionView true end - def to_json(options = nil) + def rails_to_json(options = nil) @variable end + alias to_json rails_to_json + private def append_to_function_chain!(call) @generator << @variable if @empty @@ -1211,7 +1213,7 @@ module ActionView enumerate :eachSlice, :variable => variable, :method_args => [number], :yield_args => %w(value index), :return => true, &block else add_variable_assignment!(variable) - append_enumerable_function!("eachSlice(#{number.to_json});") + append_enumerable_function!("eachSlice(#{ActiveSupport::JSON.encode(number)});") end end @@ -1232,7 +1234,7 @@ module ActionView def pluck(variable, property) add_variable_assignment!(variable) - append_enumerable_function!("pluck(#{property.to_json});") + append_enumerable_function!("pluck(#{ActiveSupport::JSON.encode(property)});") end def zip(variable, *arguments, &block) @@ -1296,7 +1298,7 @@ module ActionView class JavaScriptElementCollectionProxy < JavaScriptCollectionProxy #:nodoc:\ def initialize(generator, pattern) - super(generator, "$$(#{pattern.to_json})") + super(generator, "$$(#{ActiveSupport::JSON.encode(pattern)})") end end end diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb index e16935e..04af278 100644 --- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb +++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb @@ -43,7 +43,7 @@ module ActionView # You can change the behaviour with various options, see # http://script.aculo.us for more documentation. def visual_effect(name, element_id = false, js_options = {}) - element = element_id ? element_id.to_json : "element" + element = element_id ? ActiveSupport::JSON.encode(element_id) : "element" js_options[:queue] = if js_options[:queue].is_a?(Hash) '{' + js_options[:queue].map {|k, v| k == :limit ? "#{k}:#{v}" : "#{k}:'#{v}'" }.join(',') + '}' @@ -138,7 +138,7 @@ module ActionView end def sortable_element_js(element_id, options = {}) #:nodoc: - options[:with] ||= "Sortable.serialize(#{element_id.to_json})" + options[:with] ||= "Sortable.serialize(#{ActiveSupport::JSON.encode(element_id)})" options[:onUpdate] ||= "function(){" + remote_function(options) + "}" options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) } @@ -149,7 +149,7 @@ module ActionView options[:containment] = array_or_string_for_javascript(options[:containment]) if options[:containment] options[:only] = array_or_string_for_javascript(options[:only]) if options[:only] - %(Sortable.create(#{element_id.to_json}, #{options_for_javascript(options)});) + %(Sortable.create(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});) end # Makes the element with the DOM ID specified by +element_id+ draggable. @@ -164,7 +164,7 @@ module ActionView end def draggable_element_js(element_id, options = {}) #:nodoc: - %(new Draggable(#{element_id.to_json}, #{options_for_javascript(options)});) + %(new Draggable(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});) end # Makes the element with the DOM ID specified by +element_id+ receive @@ -219,7 +219,7 @@ module ActionView # Confirmation happens during the onDrop callback, so it can be removed from the options options.delete(:confirm) if options[:confirm] - %(Droppables.add(#{element_id.to_json}, #{options_for_javascript(options)});) + %(Droppables.add(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});) end end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 9930824..112e9ef 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -198,19 +198,19 @@ class TestController < ActionController::Base end def render_json_hello_world - render :json => {:hello => 'world'}.to_json + render :json => ActiveSupport::JSON.encode(:hello => 'world') end def render_json_hello_world_with_callback - render :json => {:hello => 'world'}.to_json, :callback => 'alert' + render :json => ActiveSupport::JSON.encode(:hello => 'world'), :callback => 'alert' end def render_json_with_custom_content_type - render :json => {:hello => 'world'}.to_json, :content_type => 'text/javascript' + render :json => ActiveSupport::JSON.encode(:hello => 'world'), :content_type => 'text/javascript' end def render_symbol_json - render :json => {:hello => 'world'}.to_json + render :json => ActiveSupport::JSON.encode(:hello => 'world') end def render_json_with_render_to_string @@ -898,31 +898,31 @@ class RenderTest < ActionController::TestCase def test_render_json get :render_json_hello_world - assert_equal '{"hello": "world"}', @response.body + assert_equal '{"hello":"world"}', @response.body assert_equal 'application/json', @response.content_type end def test_render_json_with_callback get :render_json_hello_world_with_callback - assert_equal 'alert({"hello": "world"})', @response.body + assert_equal 'alert({"hello":"world"})', @response.body assert_equal 'application/json', @response.content_type end def test_render_json_with_custom_content_type get :render_json_with_custom_content_type - assert_equal '{"hello": "world"}', @response.body + assert_equal '{"hello":"world"}', @response.body assert_equal 'text/javascript', @response.content_type end def test_render_symbol_json get :render_symbol_json - assert_equal '{"hello": "world"}', @response.body + assert_equal '{"hello":"world"}', @response.body assert_equal 'application/json', @response.content_type end def test_render_json_with_render_to_string get :render_json_with_render_to_string - assert_equal '{"hello": "partial html"}', @response.body + assert_equal '{"hello":"partial html"}', @response.body assert_equal 'application/json', @response.content_type end diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index d6b86a3..2ef4cde 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -327,28 +327,28 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest def test_remove assert_equal 'Element.remove("foo");', @generator.remove('foo') - assert_equal '["foo", "bar", "baz"].each(Element.remove);', + assert_equal '["foo","bar","baz"].each(Element.remove);', @generator.remove('foo', 'bar', 'baz') end def test_show assert_equal 'Element.show("foo");', @generator.show('foo') - assert_equal '["foo", "bar", "baz"].each(Element.show);', + assert_equal '["foo","bar","baz"].each(Element.show);', @generator.show('foo', 'bar', 'baz') end def test_hide assert_equal 'Element.hide("foo");', @generator.hide('foo') - assert_equal '["foo", "bar", "baz"].each(Element.hide);', + assert_equal '["foo","bar","baz"].each(Element.hide);', @generator.hide('foo', 'bar', 'baz') end def test_toggle assert_equal 'Element.toggle("foo");', @generator.toggle('foo') - assert_equal '["foo", "bar", "baz"].each(Element.toggle);', + assert_equal '["foo","bar","baz"].each(Element.toggle);', @generator.toggle('foo', 'bar', 'baz') end @@ -385,7 +385,7 @@ class JavaScriptGeneratorTest < PrototypeHelperBaseTest assert_equal <<-EOS.chomp, @generator.to_s Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" }); Element.insert("element", { bottom: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" }); -["foo", "bar"].each(Element.remove); +["foo","bar"].each(Element.remove); Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E"); EOS end @@ -554,8 +554,8 @@ return (value.className == "welcome"); end assert_equal <<-EOS.strip, @generator.to_s -var a = [1, 2, 3].zip([4, 5, 6], [7, 8, 9]); -var b = [1, 2, 3].zip([4, 5, 6], [7, 8, 9], function(array) { +var a = [1, 2, 3].zip([4,5,6], [7,8,9]); +var b = [1, 2, 3].zip([4,5,6], [7,8,9], function(array) { return array.reverse(); }); EOS @@ -606,7 +606,7 @@ return value.reverse(); def test_literal literal = @generator.literal("function() {}") - assert_equal "function() {}", literal.to_json + assert_equal "function() {}", ActiveSupport::JSON.encode(literal) assert_equal "", @generator.to_s end diff --git a/activerecord/lib/active_record/serializers/json_serializer.rb b/activerecord/lib/active_record/serializers/json_serializer.rb index 1fd65ed..836190e 100644 --- a/activerecord/lib/active_record/serializers/json_serializer.rb +++ b/activerecord/lib/active_record/serializers/json_serializer.rb @@ -72,13 +72,17 @@ module ActiveRecord #:nodoc: # {"comments": [{"body": "Don't think too hard"}], # "title": "So I was thinking"}]} def to_json(options = {}) + json = JsonSerializer.new(self, options).to_s if include_root_in_json - "{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}" + "{#{self.class.json_class_name}:#{json}}" else - JsonSerializer.new(self, options).to_s + json end end + # For compatibility with ActiveSupport::JSON.encode + alias rails_to_json to_json + def from_json(json) self.attributes = ActiveSupport::JSON.decode(json) self @@ -86,7 +90,7 @@ module ActiveRecord #:nodoc: class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc: def serialize - serializable_record.to_json + ActiveSupport::JSON.encode(serializable_record) end end diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb index 975acde..0ffbc9b 100644 --- a/activerecord/test/cases/json_serialization_test.rb +++ b/activerecord/test/cases/json_serialization_test.rb @@ -26,19 +26,19 @@ class JsonSerializationTest < ActiveRecord::TestCase NamespacedContact.include_root_in_json = true @contact = NamespacedContact.new :name => 'whatever' json = @contact.to_json - assert_match %r{^\{"namespaced_contact": \{}, json + assert_match %r{^\{"namespaced_contact":\{}, json end def test_should_include_root_in_json Contact.include_root_in_json = true json = @contact.to_json - assert_match %r{^\{"contact": \{}, json - assert_match %r{"name": "Konata Izumi"}, json - assert_match %r{"age": 16}, json - assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_match %r{"awesome": true}, json - assert_match %r{"preferences": \{"shows": "anime"\}}, json + assert_match %r{^\{"contact":\{}, json + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"age":16}, json + assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_match %r{"awesome":true}, json + assert_match %r{"preferences":\{"shows":"anime"\}}, json ensure Contact.include_root_in_json = false end @@ -46,31 +46,31 @@ class JsonSerializationTest < ActiveRecord::TestCase def test_should_encode_all_encodable_attributes json = @contact.to_json - assert_match %r{"name": "Konata Izumi"}, json - assert_match %r{"age": 16}, json - assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_match %r{"awesome": true}, json - assert_match %r{"preferences": \{"shows": "anime"\}}, json + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"age":16}, json + assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_match %r{"awesome":true}, json + assert_match %r{"preferences":\{"shows":"anime"\}}, json end def test_should_allow_attribute_filtering_with_only json = @contact.to_json(:only => [:name, :age]) - assert_match %r{"name": "Konata Izumi"}, json - assert_match %r{"age": 16}, json - assert_no_match %r{"awesome": true}, json - assert !json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_no_match %r{"preferences": \{"shows": "anime"\}}, json + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"age":16}, json + assert_no_match %r{"awesome":true}, json + assert !json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_no_match %r{"preferences":\{"shows":"anime"\}}, json end def test_should_allow_attribute_filtering_with_except json = @contact.to_json(:except => [:name, :age]) - assert_no_match %r{"name": "Konata Izumi"}, json - assert_no_match %r{"age": 16}, json - assert_match %r{"awesome": true}, json - assert json.include?(%("created_at": #{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_match %r{"preferences": \{"shows": "anime"\}}, json + assert_no_match %r{"name":"Konata Izumi"}, json + assert_no_match %r{"age":16}, json + assert_match %r{"awesome":true}, json + assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_match %r{"preferences":\{"shows":"anime"\}}, json end def test_methods_are_called_on_object @@ -79,12 +79,12 @@ class JsonSerializationTest < ActiveRecord::TestCase def @contact.favorite_quote; "Constraints are liberating"; end # Single method. - assert_match %r{"label": "Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label) + assert_match %r{"label":"Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label) # Both methods. methods_json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote]) - assert_match %r{"label": "Has cheezburger"}, methods_json - assert_match %r{"favorite_quote": "Constraints are liberating"}, methods_json + assert_match %r{"label":"Has cheezburger"}, methods_json + assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json end end @@ -99,42 +99,42 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase def test_includes_uses_association_name json = @david.to_json(:include => :posts) - assert_match %r{"posts": \[}, json + assert_match %r{"posts":\[}, json - assert_match %r{"id": 1}, json - assert_match %r{"name": "David"}, json + assert_match %r{"id":1}, json + assert_match %r{"name":"David"}, json - assert_match %r{"author_id": 1}, json - assert_match %r{"title": "Welcome to the weblog"}, json - assert_match %r{"body": "Such a lovely day"}, json + assert_match %r{"author_id":1}, json + assert_match %r{"title":"Welcome to the weblog"}, json + assert_match %r{"body":"Such a lovely day"}, json - assert_match %r{"title": "So I was thinking"}, json - assert_match %r{"body": "Like I hopefully always am"}, json + assert_match %r{"title":"So I was thinking"}, json + assert_match %r{"body":"Like I hopefully always am"}, json end def test_includes_uses_association_name_and_applies_attribute_filters json = @david.to_json(:include => { :posts => { :only => :title } }) - assert_match %r{"name": "David"}, json - assert_match %r{"posts": \[}, json + assert_match %r{"name":"David"}, json + assert_match %r{"posts":\[}, json - assert_match %r{"title": "Welcome to the weblog"}, json - assert_no_match %r{"body": "Such a lovely day"}, json + assert_match %r{"title":"Welcome to the weblog"}, json + assert_no_match %r{"body":"Such a lovely day"}, json - assert_match %r{"title": "So I was thinking"}, json - assert_no_match %r{"body": "Like I hopefully always am"}, json + assert_match %r{"title":"So I was thinking"}, json + assert_no_match %r{"body":"Like I hopefully always am"}, json end def test_includes_fetches_second_level_associations json = @david.to_json(:include => { :posts => { :include => { :comments => { :only => :body } } } }) - assert_match %r{"name": "David"}, json - assert_match %r{"posts": \[}, json + assert_match %r{"name":"David"}, json + assert_match %r{"posts":\[}, json - assert_match %r{"comments": \[}, json - assert_match %r{\{"body": "Thank you again for the welcome"\}}, json - assert_match %r{\{"body": "Don't think too hard"\}}, json - assert_no_match %r{"post_id": }, json + assert_match %r{"comments":\[}, json + assert_match %r{\{"body":"Thank you again for the welcome"\}}, json + assert_match %r{\{"body":"Don't think too hard"\}}, json + assert_no_match %r{"post_id":}, json end def test_includes_fetches_nth_level_associations @@ -151,11 +151,11 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase } }) - assert_match %r{"name": "David"}, json - assert_match %r{"posts": \[}, json + assert_match %r{"name":"David"}, json + assert_match %r{"posts":\[}, json - assert_match %r{"taggings": \[}, json - assert_match %r{"tag": \{"name": "General"\}}, json + assert_match %r{"taggings":\[}, json + assert_match %r{"tag":\{"name":"General"\}}, json end def test_should_not_call_methods_on_associations_that_dont_respond @@ -163,20 +163,20 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase json = @david.to_json(:include => :posts, :methods => :favorite_quote) assert !@david.posts.first.respond_to?(:favorite_quote) - assert_match %r{"favorite_quote": "Constraints are liberating"}, json - assert_equal %r{"favorite_quote": }.match(json).size, 1 + assert_match %r{"favorite_quote":"Constraints are liberating"}, json + assert_equal %r{"favorite_quote":}.match(json).size, 1 end def test_should_allow_only_option_for_list_of_authors authors = [@david, @mary] - assert_equal %([{"name": "David"}, {"name": "Mary"}]), authors.to_json(:only => :name) + assert_equal %([{"name":"David"},{"name":"Mary"}]), authors.to_json(:only => :name) end def test_should_allow_except_option_for_list_of_authors authors = [@david, @mary] - assert_equal %([{"id": 1}, {"id": 2}]), authors.to_json(:except => [:name, :author_address_id, :author_address_extra_id]) + assert_equal %([{"id":1},{"id":2}]), authors.to_json(:except => [:name, :author_address_id, :author_address_extra_id]) end def test_should_allow_includes_for_list_of_authors @@ -188,8 +188,8 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase } ) - ['"name": "David"', '"posts": [', '{"id": 1}', '{"id": 2}', '{"id": 4}', - '{"id": 5}', '{"id": 6}', '"name": "Mary"', '"posts": [{"id": 7}]'].each do |fragment| + ['"name":"David"', '"posts":[', '{"id":1}', '{"id":2}', '{"id":4}', + '{"id":5}', '{"id":6}', '"name":"Mary"', '"posts":[{"id":7}]'].each do |fragment| assert json.include?(fragment), json end end @@ -200,6 +200,6 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase 2 => @mary } - assert_equal %({"1": {"name": "David"}}), authors_hash.to_json(:only => [1, :name]) + assert_equal %({"1":{"name":"David"}}), authors_hash.to_json(:only => [1, :name]) end end diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 8ff7ab4..b04f705 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -894,9 +894,12 @@ module ActiveResource # person.to_json(:except => ["first_name"]) # # => {"last_name": "Smith"} def to_json(options={}) - attributes.to_json(options) + ActiveSupport::JSON.encode(attributes, options) end + # For compatibility with ActiveSupport::JSON.encode + alias rails_to_json to_json + # Returns the serialized string representation of the resource in the configured # serialization format specified in ActiveResource::Base.format. The options # applicable depend on the configured encoding format. diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb index 2bdb4a7..ee3cde6 100644 --- a/activesupport/lib/active_support/json.rb +++ b/activesupport/lib/active_support/json.rb @@ -1,6 +1,34 @@ module ActiveSupport # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format. mattr_accessor :use_standard_json_time_format + # Look for and parse json strings that look like ISO 8601 times. + mattr_accessor :parse_json_times + + module JSON + # matches YAML-formatted dates + DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/ + + class << self + attr_reader :backend + delegate :decode, :to => :backend + + def backend=(name) + if name.is_a?(Module) + @backend = name + else + require "active_support/json/backends/#{name.to_s.downcase}.rb" + @backend = ActiveSupport::JSON::Backends::const_get(name) + end + end + + def with_backend(name) + old_backend, self.backend = backend, name + yield + ensure + self.backend = old_backend + end + end + end class << self def escape_html_entities_in_json @@ -17,7 +45,8 @@ module ActiveSupport @escape_html_entities_in_json = value end end + + JSON.backend = 'Yaml' end -require 'active_support/json/encoding' -require 'active_support/json/decoding' +require 'active_support/json/encoding' \ No newline at end of file diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb new file mode 100644 index 0000000..de847e3 --- /dev/null +++ b/activesupport/lib/active_support/json/backends/jsongem.rb @@ -0,0 +1,36 @@ +module ActiveSupport + module JSON + ParseError = ::JSON::ParserError + + module Backends + module JSONGem + extend self + + # Converts a JSON string into a Ruby object. + def decode(json) + data = ::JSON.parse(json) + if ActiveSupport.parse_json_times + convert_dates_from(data) + else + data + end + end + + private + def convert_dates_from(data) + case data + when DATE_REGEX + DateTime.parse(data) + when Array + data.map! { |d| convert_dates_from(d) } + when Hash + data.each do |key, value| + data[key] = convert_dates_from(value) + end + else data + end + end + end + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb new file mode 100644 index 0000000..97dd468 --- /dev/null +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -0,0 +1,83 @@ +require 'active_support/core_ext/string/starts_ends_with' + +module ActiveSupport + module JSON + class ParseError < StandardError + end + + module Backends + module Yaml + extend self + + # Converts a JSON string into a Ruby object. + def decode(json) + YAML.load(convert_json_to_yaml(json)) + rescue ArgumentError => e + raise ParseError, "Invalid JSON string" + end + + protected + # Ensure that ":" and "," are always followed by a space + def convert_json_to_yaml(json) #:nodoc: + require 'strscan' unless defined? ::StringScanner + scanner, quoting, marks, pos, times = ::StringScanner.new(json), false, [], nil, [] + while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/) + case char = scanner[1] + when '"', "'" + if !quoting + quoting = char + pos = scanner.pos + elsif quoting == char + if json[pos..scanner.pos-2] =~ DATE_REGEX + # found a date, track the exact positions of the quotes so we can remove them later. + # oh, and increment them for each current mark, each one is an extra padded space that bumps + # the position in the final YAML output + total_marks = marks.size + times << pos+total_marks << scanner.pos+total_marks + end + quoting = false + end + when ":","," + marks << scanner.pos - 1 unless quoting + end + end + + if marks.empty? + json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do + ustr = $1 + if ustr.start_with?('u') + [ustr[1..-1].to_i(16)].pack("U") + elsif ustr == '\\' + '\\\\' + else + ustr + end + end + else + left_pos = [-1].push(*marks) + right_pos = marks << scanner.pos + scanner.rest_size + output = [] + left_pos.each_with_index do |left, i| + scanner.pos = left.succ + output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do + ustr = $1 + if ustr.start_with?('u') + [ustr[1..-1].to_i(16)].pack("U") + elsif ustr == '\\' + '\\\\' + else + ustr + end + end + end + output = output * " " + + times.each { |i| output[i-1] = ' ' } + output.gsub!(/\\\//, '/') + output + end + end + end + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb deleted file mode 100644 index 0e07934..0000000 --- a/activesupport/lib/active_support/json/decoding.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'yaml' -require 'strscan' - -module ActiveSupport - module JSON - class ParseError < StandardError - end - - class << self - # Converts a JSON string into a Ruby object. - def decode(json) - YAML.load(convert_json_to_yaml(json)) - rescue ArgumentError => e - raise ParseError, "Invalid JSON string" - end - - protected - # matches YAML-formatted dates - DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)$/ - - # Ensure that ":" and "," are always followed by a space - def convert_json_to_yaml(json) #:nodoc: - scanner, quoting, marks, pos, times = StringScanner.new(json), false, [], nil, [] - while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/) - case char = scanner[1] - when '"', "'" - if !quoting - quoting = char - pos = scanner.pos - elsif quoting == char - if json[pos..scanner.pos-2] =~ DATE_REGEX - # found a date, track the exact positions of the quotes so we can remove them later. - # oh, and increment them for each current mark, each one is an extra padded space that bumps - # the position in the final YAML output - total_marks = marks.size - times << pos+total_marks << scanner.pos+total_marks - end - quoting = false - end - when ":","," - marks << scanner.pos - 1 unless quoting - end - end - - if marks.empty? - json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do - ustr = $1 - if ustr.starts_with?('u') - [ustr[1..-1].to_i(16)].pack("U") - elsif ustr == '\\' - '\\\\' - else - ustr - end - end - else - left_pos = [-1].push(*marks) - right_pos = marks << scanner.pos + scanner.rest_size - output = [] - left_pos.each_with_index do |left, i| - scanner.pos = left.succ - output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do - ustr = $1 - if ustr.starts_with?('u') - [ustr[1..-1].to_i(16)].pack("U") - elsif ustr == '\\' - '\\\\' - else - ustr - end - end - end - output = output * " " - - times.each { |i| output[i-1] = ' ' } - output.gsub!(/\\\//, '/') - output - end - end - end - end -end diff --git a/activesupport/lib/active_support/json/encoders/date.rb b/activesupport/lib/active_support/json/encoders/date.rb index cc84de1..79c3957 100644 --- a/activesupport/lib/active_support/json/encoders/date.rb +++ b/activesupport/lib/active_support/json/encoders/date.rb @@ -11,11 +11,13 @@ class Date # # With ActiveSupport.use_standard_json_time_format = false # Date.new(2005,2,1).to_json # # => "2005/02/01" - def to_json(options = nil) + def rails_to_json(options = nil) if ActiveSupport.use_standard_json_time_format %("#{strftime("%Y-%m-%d")}") else %("#{strftime("%Y/%m/%d")}") end end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb index 6c85824..cdfc39b 100644 --- a/activesupport/lib/active_support/json/encoders/date_time.rb +++ b/activesupport/lib/active_support/json/encoders/date_time.rb @@ -11,11 +11,13 @@ class DateTime # # With ActiveSupport.use_standard_json_time_format = false # DateTime.civil(2005,2,1,15,15,10).to_json # # => "2005/02/01 15:15:10 +0000" - def to_json(options = nil) + def rails_to_json(options = nil) if ActiveSupport.use_standard_json_time_format xmlschema.inspect else strftime('"%Y/%m/%d %H:%M:%S %z"') end end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/enumerable.rb b/activesupport/lib/active_support/json/encoders/enumerable.rb index 881b1d6..e1c3ec2 100644 --- a/activesupport/lib/active_support/json/encoders/enumerable.rb +++ b/activesupport/lib/active_support/json/encoders/enumerable.rb @@ -6,7 +6,9 @@ module Enumerable # # => users.to_json(:only => :name) # # will pass the :only => :name option to each user. - def to_json(options = {}) #:nodoc: - "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ', '}]" + def rails_to_json(options = {}) #:nodoc: + "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ','}]" end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/false_class.rb b/activesupport/lib/active_support/json/encoders/false_class.rb index bf08443..a7657cc 100644 --- a/activesupport/lib/active_support/json/encoders/false_class.rb +++ b/activesupport/lib/active_support/json/encoders/false_class.rb @@ -1,5 +1,7 @@ class FalseClass - def to_json(options = nil) #:nodoc: + def rails_to_json(options = nil) #:nodoc: 'false' end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/hash.rb b/activesupport/lib/active_support/json/encoders/hash.rb index e38b4f3..bc58b33 100644 --- a/activesupport/lib/active_support/json/encoders/hash.rb +++ b/activesupport/lib/active_support/json/encoders/hash.rb @@ -28,7 +28,7 @@ class Hash # would pass the :include => :posts option to users, # allowing the posts association in the User model to be converted to JSON # as well. - def to_json(options = {}) #:nodoc: + def rails_to_json(options = {}) #:nodoc: hash_keys = self.keys if except = options[:except] @@ -39,8 +39,10 @@ class Hash result = '{' result << hash_keys.map do |key| - "#{ActiveSupport::JSON.encode(key.to_s)}: #{ActiveSupport::JSON.encode(self[key], options)}" - end * ', ' + "#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(self[key], options)}" + end * ',' result << '}' end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/nil_class.rb b/activesupport/lib/active_support/json/encoders/nil_class.rb index 4763471..b31e1dd 100644 --- a/activesupport/lib/active_support/json/encoders/nil_class.rb +++ b/activesupport/lib/active_support/json/encoders/nil_class.rb @@ -1,5 +1,7 @@ class NilClass - def to_json(options = nil) #:nodoc: + def rails_to_json(options = nil) #:nodoc: 'null' end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/numeric.rb b/activesupport/lib/active_support/json/encoders/numeric.rb index 38713fb..491b330 100644 --- a/activesupport/lib/active_support/json/encoders/numeric.rb +++ b/activesupport/lib/active_support/json/encoders/numeric.rb @@ -1,5 +1,7 @@ class Numeric - def to_json(options = nil) #:nodoc: + def rails_to_json(options = nil) #:nodoc: to_s end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/object.rb b/activesupport/lib/active_support/json/encoders/object.rb index ca215d4..5556eb5 100644 --- a/activesupport/lib/active_support/json/encoders/object.rb +++ b/activesupport/lib/active_support/json/encoders/object.rb @@ -1,6 +1,8 @@ class Object # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. - def to_json(options = {}) + def rails_to_json(options = {}) ActiveSupport::JSON.encode(instance_values, options) end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/regexp.rb b/activesupport/lib/active_support/json/encoders/regexp.rb index b6116b7..63ccd7c 100644 --- a/activesupport/lib/active_support/json/encoders/regexp.rb +++ b/activesupport/lib/active_support/json/encoders/regexp.rb @@ -1,5 +1,7 @@ class Regexp - def to_json(options = nil) #:nodoc: + def rails_to_json(options = nil) #:nodoc: inspect end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/string.rb b/activesupport/lib/active_support/json/encoders/string.rb index 5ef7979..27bef3b 100644 --- a/activesupport/lib/active_support/json/encoders/string.rb +++ b/activesupport/lib/active_support/json/encoders/string.rb @@ -22,7 +22,7 @@ end ActiveSupport.escape_html_entities_in_json = true class String - def to_json(options = nil) #:nodoc: + def rails_to_json(options = nil) #:nodoc: json = '"' + gsub(ActiveSupport::JSON::Encoding.escape_regex) { |s| ActiveSupport::JSON::Encoding::ESCAPED_CHARS[s] } @@ -33,4 +33,6 @@ class String s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') } + '"' end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/symbol.rb b/activesupport/lib/active_support/json/encoders/symbol.rb index 485112f..6487bf8 100644 --- a/activesupport/lib/active_support/json/encoders/symbol.rb +++ b/activesupport/lib/active_support/json/encoders/symbol.rb @@ -1,5 +1,7 @@ class Symbol - def to_json(options = {}) #:nodoc: + def rails_to_json(options = {}) #:nodoc: ActiveSupport::JSON.encode(to_s, options) end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/time.rb b/activesupport/lib/active_support/json/encoders/time.rb index f45a005..786273a 100644 --- a/activesupport/lib/active_support/json/encoders/time.rb +++ b/activesupport/lib/active_support/json/encoders/time.rb @@ -11,11 +11,13 @@ class Time # # With ActiveSupport.use_standard_json_time_format = false # Time.utc(2005,2,1,15,15,10).to_json # # => "2005/02/01 15:15:10 +0000" - def to_json(options = nil) + def rails_to_json(options = nil) if ActiveSupport.use_standard_json_time_format xmlschema.inspect else %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") end end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/true_class.rb b/activesupport/lib/active_support/json/encoders/true_class.rb index 037d812..ac7c7d1 100644 --- a/activesupport/lib/active_support/json/encoders/true_class.rb +++ b/activesupport/lib/active_support/json/encoders/true_class.rb @@ -1,5 +1,7 @@ class TrueClass - def to_json(options = nil) #:nodoc: + def rails_to_json(options = nil) #:nodoc: 'true' end + + alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index aaaa3cd..2aa5ffc 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -8,7 +8,7 @@ module ActiveSupport seen = (options[:seen] ||= []) raise CircularReferenceError, 'object references itself' if seen.include?(value) seen << value - value.send(:to_json, options) + value.rails_to_json(options) ensure seen.pop end diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb index 7fd23b0..9924168 100644 --- a/activesupport/lib/active_support/json/variable.rb +++ b/activesupport/lib/active_support/json/variable.rb @@ -2,9 +2,11 @@ module ActiveSupport module JSON # A string that returns itself as its JSON-encoded form. class Variable < String - def to_json(options=nil) + def rails_to_json(options=nil) self end + + alias to_json rails_to_json end end end diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 518ca77..5572f61 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -120,7 +120,7 @@ module ActiveSupport # # With ActiveSupport.use_standard_json_time_format = false # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json # # => "2005/02/01 15:15:10 +0000" - def to_json(options = nil) + def rails_to_json(options = nil) if ActiveSupport.use_standard_json_time_format xmlschema.inspect else @@ -128,6 +128,8 @@ module ActiveSupport end end + alias to_json rails_to_json + def to_yaml(options = {}) if options.kind_of?(YAML::Emitter) utc.to_yaml(options) diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index accfe51..03ed783 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -55,12 +55,12 @@ class TimeWithZoneTest < Test::Unit::TestCase end def test_to_json - assert_equal "\"1999/12/31 19:00:00 -0500\"", @twz.to_json + assert_equal "\"1999/12/31 19:00:00 -0500\"", ActiveSupport::JSON.encode(@twz) end def test_to_json_with_use_standard_json_time_format_config_set_to_true old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, true - assert_equal "\"1999-12-31T19:00:00-05:00\"", @twz.to_json + assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(@twz) ensure ActiveSupport.use_standard_json_time_format = old end diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index 8fe4055..7ffd3f8 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -1,49 +1,74 @@ # encoding: UTF-8 require 'abstract_unit' -class TestJSONDecoding < Test::Unit::TestCase +class TestJSONDecoding < ActiveSupport::TestCase TESTS = { %q({"returnTo":{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}}, - %q({returnTo:{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}}, %q({"return\\"To\\":":{"\/categories":"\/"}}) => {"return\"To\":" => {"/categories" => "/"}}, %q({"returnTo":{"\/categories":1}}) => {"returnTo" => {"/categories" => 1}}, %({"returnTo":[1,"a"]}) => {"returnTo" => [1, "a"]}, %({"returnTo":[1,"\\"a\\",", "b"]}) => {"returnTo" => [1, "\"a\",", "b"]}, - %({a: "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"}, - %({a: "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"}, + %({"a": "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"}, + %({"a": "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"}, # multibyte %({"matzue": "松江", "asakusa": "浅草"}) => {"matzue" => "松江", "asakusa" => "浅草"}, - %({a: "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)}, - %({a: "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)}, + %({"a": "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)}, + %({"a": "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)}, # no time zone - %({a: "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"}, + %({"a": "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"}, # needs to be *exact* - %({a: " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "}, - %({a: "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"}, + %({"a": " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "}, + %({"a": "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"}, %([]) => [], %({}) => {}, - %(1) => 1, - %("") => "", - %("\\"") => "\"", - %(null) => nil, - %(true) => true, - %(false) => false, - %q("http:\/\/test.host\/posts\/1") => "http://test.host/posts/1", - %q("\u003cunicode\u0020escape\u003e") => "", - %q("\\\\u0020skip double backslashes") => "\\u0020skip double backslashes", - %q({a: "\u003cbr /\u003e"}) => {'a' => "
"}, - %q({b:["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["","",""]} + %({"a":1}) => {"a" => 1}, + %({"a": ""}) => {"a" => ""}, + %({"a":"\\""}) => {"a" => "\""}, + %({"a": null}) => {"a" => nil}, + %({"a": true}) => {"a" => true}, + %({"a": false}) => {"a" => false}, + %q({"a": "http:\/\/test.host\/posts\/1"}) => {"a" => "http://test.host/posts/1"}, + %q({"a": "\u003cunicode\u0020escape\u003e"}) => {"a" => ""}, + %q({"a": "\\\\u0020skip double backslashes"}) => {"a" => "\\u0020skip double backslashes"}, + %q({"a": "\u003cbr /\u003e"}) => {'a' => "
"}, + %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["","",""]} } - - TESTS.each do |json, expected| - define_method :"test_json_decoding_#{json}" do - assert_nothing_raised do - assert_equal expected, ActiveSupport::JSON.decode(json) + + backends = %w(Yaml) + begin + gem 'json', '>= 1.1' + require 'json' + backends << "JSONGem" + rescue Gem::LoadError + # Skip JSON gem tests + end + + backends.each do |backend| + TESTS.each do |json, expected| + test "json decodes #{json} with the #{backend} backend" do + ActiveSupport.parse_json_times = true + silence_warnings do + ActiveSupport::JSON.with_backend backend do + assert_nothing_raised do + assert_equal expected, ActiveSupport::JSON.decode(json) + end + end + end + end + end + end + + if backends.include?("JSONGem") + test "json decodes time json with time parsing disabled" do + ActiveSupport.parse_json_times = false + expected = {"a" => "2007-01-01 01:12:34 Z"} + ActiveSupport::JSON.with_backend "JSONGem" do + assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"})) end end end - + def test_failed_json_decoding assert_raise(ActiveSupport::JSON::ParseError) { ActiveSupport::JSON.decode(%({: 1})) } end -end +end \ No newline at end of file diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 7d2eeda..823eab0 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -18,14 +18,14 @@ class TestJSONEncoding < Test::Unit::TestCase [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ], [ 'http://test.host/posts/1', %("http://test.host/posts/1")]] - ArrayTests = [[ ['a', 'b', 'c'], %([\"a\", \"b\", \"c\"]) ], - [ [1, 'a', :b, nil, false], %([1, \"a\", \"b\", null, false]) ]] + ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ], + [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]] SymbolTests = [[ :a, %("a") ], [ :this, %("this") ], [ :"a b", %("a b") ]] - ObjectTests = [[ Foo.new(1, 2), %({\"a\": 1, \"b\": 2}) ]] + ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]] VariableTests = [[ ActiveSupport::JSON::Variable.new('foo'), 'foo'], [ ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")']] @@ -46,7 +46,7 @@ class TestJSONEncoding < Test::Unit::TestCase ActiveSupport.escape_html_entities_in_json = class_tests !~ /^Standard/ ActiveSupport.use_standard_json_time_format = class_tests =~ /^Standard/ self.class.const_get(class_tests).each do |pair| - assert_equal pair.last, pair.first.to_json + assert_equal pair.last, ActiveSupport::JSON.encode(pair.first) end ensure ActiveSupport.escape_html_entities_in_json = false @@ -56,45 +56,45 @@ class TestJSONEncoding < Test::Unit::TestCase end def test_hash_encoding - assert_equal %({\"a\": \"b\"}), { :a => :b }.to_json - assert_equal %({\"a\": 1}), { 'a' => 1 }.to_json - assert_equal %({\"a\": [1, 2]}), { 'a' => [1,2] }.to_json - assert_equal %({"1": 2}), { 1 => 2 }.to_json + assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(:a => :b) + assert_equal %({\"a\":1}), ActiveSupport::JSON.encode('a' => 1) + assert_equal %({\"a\":[1,2]}), ActiveSupport::JSON.encode('a' => [1,2]) + assert_equal %({"1":2}), ActiveSupport::JSON.encode(1 => 2) - sorted_json = '{' + {:a => :b, :c => :d}.to_json[1..-2].split(', ').sort.join(', ') + '}' - assert_equal %({\"a\": \"b\", \"c\": \"d\"}), sorted_json + sorted_json = '{' + ActiveSupport::JSON.encode(:a => :b, :c => :d)[1..-2].split(',').sort.join(',') + '}' + assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json end def test_utf8_string_encoded_properly_when_kcode_is_utf8 with_kcode 'UTF8' do - assert_equal '"\\u20ac2.99"', '€2.99'.to_json - assert_equal '"\\u270e\\u263a"', '✎☺'.to_json + assert_equal '"\\u20ac2.99"', ActiveSupport::JSON.encode('€2.99') + assert_equal '"\\u270e\\u263a"', ActiveSupport::JSON.encode('✎☺') end end def test_exception_raised_when_encoding_circular_reference a = [1] a << a - assert_raise(ActiveSupport::JSON::CircularReferenceError) { a.to_json } + assert_raise(ActiveSupport::JSON::CircularReferenceError) { ActiveSupport::JSON.encode(a) } end def test_hash_key_identifiers_are_always_quoted values = {0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B"} - assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(values.to_json) + assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values)) end def test_hash_should_allow_key_filtering_with_only - assert_equal %({"a": 1}), { 'a' => 1, :b => 2, :c => 3 }.to_json(:only => 'a') + assert_equal %({"a":1}), ActiveSupport::JSON.encode({'a' => 1, :b => 2, :c => 3}, :only => 'a') end def test_hash_should_allow_key_filtering_with_except - assert_equal %({"b": 2}), { 'foo' => 'bar', :b => 2, :c => 3 }.to_json(:except => ['foo', :c]) + assert_equal %({"b":2}), ActiveSupport::JSON.encode({'foo' => 'bar', :b => 2, :c => 3}, :except => ['foo', :c]) end def test_time_to_json_includes_local_offset ActiveSupport.use_standard_json_time_format = true with_env_tz 'US/Eastern' do - assert_equal %("2005-02-01T15:15:10-05:00"), Time.local(2005,2,1,15,15,10).to_json + assert_equal %("2005-02-01T15:15:10-05:00"), ActiveSupport::JSON.encode(Time.local(2005,2,1,15,15,10)) end ensure ActiveSupport.use_standard_json_time_format = false @@ -108,7 +108,7 @@ class TestJSONEncoding < Test::Unit::TestCase :latitude => 123.234 } } - result = hash.to_json + result = ActiveSupport::JSON.encode(hash) end end @@ -133,6 +133,6 @@ class JsonOptionsTests < Test::Unit::TestCase ActiveSupport::JSON.expects(:encode).with(2, json_options) ActiveSupport::JSON.expects(:encode).with('foo', json_options) - [1, 2, 'foo'].to_json(json_options) + [1, 2, 'foo'].rails_to_json(json_options) end end -- 1.6.4.2 From cc47d3ff0c5e9d20b2590354a9992fbb0ea80e10 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 26 Apr 2009 15:18:33 -0700 Subject: [PATCH 060/779] Only Object to_json alias is needed. Prefer nil options. --- .../lib/action_view/helpers/prototype_helper.rb | 2 -- .../lib/active_support/json/encoders/date.rb | 2 -- .../lib/active_support/json/encoders/date_time.rb | 2 -- .../lib/active_support/json/encoders/enumerable.rb | 4 +--- .../active_support/json/encoders/false_class.rb | 2 -- .../lib/active_support/json/encoders/hash.rb | 14 +++++++------- .../lib/active_support/json/encoders/nil_class.rb | 2 -- .../lib/active_support/json/encoders/numeric.rb | 2 -- .../lib/active_support/json/encoders/object.rb | 2 +- .../lib/active_support/json/encoders/regexp.rb | 2 -- .../lib/active_support/json/encoders/string.rb | 2 -- .../lib/active_support/json/encoders/symbol.rb | 4 +--- .../lib/active_support/json/encoders/time.rb | 2 -- .../lib/active_support/json/encoders/true_class.rb | 2 -- activesupport/lib/active_support/json/encoding.rb | 3 ++- 15 files changed, 12 insertions(+), 35 deletions(-) diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 0502e17..e40c5b5 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -1188,8 +1188,6 @@ module ActionView @variable end - alias to_json rails_to_json - private def append_to_function_chain!(call) @generator << @variable if @empty diff --git a/activesupport/lib/active_support/json/encoders/date.rb b/activesupport/lib/active_support/json/encoders/date.rb index 79c3957..1aebdd2 100644 --- a/activesupport/lib/active_support/json/encoders/date.rb +++ b/activesupport/lib/active_support/json/encoders/date.rb @@ -18,6 +18,4 @@ class Date %("#{strftime("%Y/%m/%d")}") end end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb index cdfc39b..096e0dd 100644 --- a/activesupport/lib/active_support/json/encoders/date_time.rb +++ b/activesupport/lib/active_support/json/encoders/date_time.rb @@ -18,6 +18,4 @@ class DateTime strftime('"%Y/%m/%d %H:%M:%S %z"') end end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/enumerable.rb b/activesupport/lib/active_support/json/encoders/enumerable.rb index e1c3ec2..6e0c3b6 100644 --- a/activesupport/lib/active_support/json/encoders/enumerable.rb +++ b/activesupport/lib/active_support/json/encoders/enumerable.rb @@ -6,9 +6,7 @@ module Enumerable # # => users.to_json(:only => :name) # # will pass the :only => :name option to each user. - def rails_to_json(options = {}) #:nodoc: + def rails_to_json(options = nil) #:nodoc: "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ','}]" end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/false_class.rb b/activesupport/lib/active_support/json/encoders/false_class.rb index a7657cc..4c47b33 100644 --- a/activesupport/lib/active_support/json/encoders/false_class.rb +++ b/activesupport/lib/active_support/json/encoders/false_class.rb @@ -2,6 +2,4 @@ class FalseClass def rails_to_json(options = nil) #:nodoc: 'false' end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/hash.rb b/activesupport/lib/active_support/json/encoders/hash.rb index bc58b33..6991163 100644 --- a/activesupport/lib/active_support/json/encoders/hash.rb +++ b/activesupport/lib/active_support/json/encoders/hash.rb @@ -28,13 +28,15 @@ class Hash # would pass the :include => :posts option to users, # allowing the posts association in the User model to be converted to JSON # as well. - def rails_to_json(options = {}) #:nodoc: + def rails_to_json(options = nil) #:nodoc: hash_keys = self.keys - if except = options[:except] - hash_keys = hash_keys - Array.wrap(except) - elsif only = options[:only] - hash_keys = hash_keys & Array.wrap(only) + if options + if except = options[:except] + hash_keys = hash_keys - Array.wrap(except) + elsif only = options[:only] + hash_keys = hash_keys & Array.wrap(only) + end end result = '{' @@ -43,6 +45,4 @@ class Hash end * ',' result << '}' end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/nil_class.rb b/activesupport/lib/active_support/json/encoders/nil_class.rb index b31e1dd..b7b63e2 100644 --- a/activesupport/lib/active_support/json/encoders/nil_class.rb +++ b/activesupport/lib/active_support/json/encoders/nil_class.rb @@ -2,6 +2,4 @@ class NilClass def rails_to_json(options = nil) #:nodoc: 'null' end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/numeric.rb b/activesupport/lib/active_support/json/encoders/numeric.rb index 491b330..b969902 100644 --- a/activesupport/lib/active_support/json/encoders/numeric.rb +++ b/activesupport/lib/active_support/json/encoders/numeric.rb @@ -2,6 +2,4 @@ class Numeric def rails_to_json(options = nil) #:nodoc: to_s end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/object.rb b/activesupport/lib/active_support/json/encoders/object.rb index 5556eb5..79d556d 100644 --- a/activesupport/lib/active_support/json/encoders/object.rb +++ b/activesupport/lib/active_support/json/encoders/object.rb @@ -1,6 +1,6 @@ class Object # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. - def rails_to_json(options = {}) + def rails_to_json(options = nil) ActiveSupport::JSON.encode(instance_values, options) end diff --git a/activesupport/lib/active_support/json/encoders/regexp.rb b/activesupport/lib/active_support/json/encoders/regexp.rb index 63ccd7c..ff7dd67 100644 --- a/activesupport/lib/active_support/json/encoders/regexp.rb +++ b/activesupport/lib/active_support/json/encoders/regexp.rb @@ -2,6 +2,4 @@ class Regexp def rails_to_json(options = nil) #:nodoc: inspect end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/string.rb b/activesupport/lib/active_support/json/encoders/string.rb index 27bef3b..da16111 100644 --- a/activesupport/lib/active_support/json/encoders/string.rb +++ b/activesupport/lib/active_support/json/encoders/string.rb @@ -33,6 +33,4 @@ class String s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') } + '"' end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/symbol.rb b/activesupport/lib/active_support/json/encoders/symbol.rb index 6487bf8..c39eda8 100644 --- a/activesupport/lib/active_support/json/encoders/symbol.rb +++ b/activesupport/lib/active_support/json/encoders/symbol.rb @@ -1,7 +1,5 @@ class Symbol - def rails_to_json(options = {}) #:nodoc: + def rails_to_json(options = nil) #:nodoc: ActiveSupport::JSON.encode(to_s, options) end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/time.rb b/activesupport/lib/active_support/json/encoders/time.rb index 786273a..bd0062e 100644 --- a/activesupport/lib/active_support/json/encoders/time.rb +++ b/activesupport/lib/active_support/json/encoders/time.rb @@ -18,6 +18,4 @@ class Time %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") end end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoders/true_class.rb b/activesupport/lib/active_support/json/encoders/true_class.rb index ac7c7d1..a22c487 100644 --- a/activesupport/lib/active_support/json/encoders/true_class.rb +++ b/activesupport/lib/active_support/json/encoders/true_class.rb @@ -2,6 +2,4 @@ class TrueClass def rails_to_json(options = nil) #:nodoc: 'true' end - - alias to_json rails_to_json end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 2aa5ffc..76f8926 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -4,7 +4,8 @@ module ActiveSupport end # Converts a Ruby object into a JSON string. - def self.encode(value, options = {}) + def self.encode(value, options = nil) + options ||= {} seen = (options[:seen] ||= []) raise CircularReferenceError, 'object references itself' if seen.include?(value) seen << value -- 1.6.4.2 From 5b80ead2a3aafb3aea904b7e0f2115e1a56dc843 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 26 Apr 2009 15:53:32 -0700 Subject: [PATCH 061/779] Extract json string escaping --- activesupport/lib/active_support/json.rb | 38 ++++++++++++++++--- .../lib/active_support/json/encoders/string.rb | 33 +---------------- 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb index ee3cde6..8b4c0bd 100644 --- a/activesupport/lib/active_support/json.rb +++ b/activesupport/lib/active_support/json.rb @@ -8,6 +8,33 @@ module ActiveSupport # matches YAML-formatted dates DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/ + module Encoding #:nodoc: + mattr_accessor :escape_regex + + ESCAPED_CHARS = { + "\010" => '\b', + "\f" => '\f', + "\n" => '\n', + "\r" => '\r', + "\t" => '\t', + '"' => '\"', + '\\' => '\\\\', + '>' => '\u003E', + '<' => '\u003C', + '&' => '\u0026' + } + + def self.escape(string) + json = '"' + string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] } + json.force_encoding('ascii-8bit') if respond_to?(:force_encoding) + json.gsub(/([\xC0-\xDF][\x80-\xBF]| + [\xE0-\xEF][\x80-\xBF]{2}| + [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| + s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') + } + '"' + end + end + class << self attr_reader :backend delegate :decode, :to => :backend @@ -31,9 +58,7 @@ module ActiveSupport end class << self - def escape_html_entities_in_json - @escape_html_entities_in_json - end + attr_reader :escape_html_entities_in_json def escape_html_entities_in_json=(value) ActiveSupport::JSON::Encoding.escape_regex = \ @@ -45,8 +70,9 @@ module ActiveSupport @escape_html_entities_in_json = value end end - - JSON.backend = 'Yaml' end -require 'active_support/json/encoding' \ No newline at end of file +ActiveSupport.escape_html_entities_in_json = true +ActiveSupport::JSON.backend = 'Yaml' + +require 'active_support/json/encoding' diff --git a/activesupport/lib/active_support/json/encoders/string.rb b/activesupport/lib/active_support/json/encoders/string.rb index da16111..ea82ca2 100644 --- a/activesupport/lib/active_support/json/encoders/string.rb +++ b/activesupport/lib/active_support/json/encoders/string.rb @@ -1,36 +1,5 @@ -module ActiveSupport - module JSON - module Encoding - mattr_accessor :escape_regex - - ESCAPED_CHARS = { - "\010" => '\b', - "\f" => '\f', - "\n" => '\n', - "\r" => '\r', - "\t" => '\t', - '"' => '\"', - '\\' => '\\\\', - '>' => '\u003E', - '<' => '\u003C', - '&' => '\u0026' - } - end - end -end - -ActiveSupport.escape_html_entities_in_json = true - class String def rails_to_json(options = nil) #:nodoc: - json = '"' + gsub(ActiveSupport::JSON::Encoding.escape_regex) { |s| - ActiveSupport::JSON::Encoding::ESCAPED_CHARS[s] - } - json.force_encoding('ascii-8bit') if respond_to?(:force_encoding) - json.gsub(/([\xC0-\xDF][\x80-\xBF]| - [\xE0-\xEF][\x80-\xBF]{2}| - [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| - s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') - } + '"' + ActiveSupport::JSON::Encoding.escape(self) end end -- 1.6.4.2 From 2b5e4f38f5d7af912770f8a298ba2e5788c715b1 Mon Sep 17 00:00:00 2001 From: rick Date: Sun, 17 May 2009 19:16:11 -0700 Subject: [PATCH 062/779] load the JSON Backend lazily. --- activesupport/lib/active_support/json.rb | 11 ++++++++--- .../lib/active_support/json/backends/jsongem.rb | 4 +++- .../lib/active_support/json/backends/yaml.rb | 4 +++- activesupport/test/json/decoding_test.rb | 3 +++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb index 8b4c0bd..0b115d7 100644 --- a/activesupport/lib/active_support/json.rb +++ b/activesupport/lib/active_support/json.rb @@ -36,9 +36,15 @@ module ActiveSupport end class << self - attr_reader :backend delegate :decode, :to => :backend - + + def backend + @backend || begin + self.backend = "Yaml" + @backend + end + end + def backend=(name) if name.is_a?(Module) @backend = name @@ -73,6 +79,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 de847e3..a6da27d 100644 --- a/activesupport/lib/active_support/json/backends/jsongem.rb +++ b/activesupport/lib/active_support/json/backends/jsongem.rb @@ -1,6 +1,8 @@ +require 'json' unless defined?(JSON) + module ActiveSupport module JSON - ParseError = ::JSON::ParserError + ParseError = ::JSON::ParserError unless const_defined?(:ParseError) module Backends module JSONGem diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb index 97dd468..a2797cc 100644 --- a/activesupport/lib/active_support/json/backends/yaml.rb +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -2,7 +2,9 @@ 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 diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index 7ffd3f8..86053c9 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -34,6 +34,9 @@ class TestJSONDecoding < ActiveSupport::TestCase %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["","",""]} } + # load the default JSON backend + ActiveSupport::JSON.backend + backends = %w(Yaml) begin gem 'json', '>= 1.1' -- 1.6.4.2 From dbb025827992331843566be418a6f86d89f41868 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 18 May 2009 16:59:37 +0200 Subject: [PATCH 063/779] Ensure HTTP Digest auth uses appropriate HTTP method [#2490 state:resolved] [Steve Madsen] --- .../lib/action_controller/http_authentication.rb | 3 +- .../controller/http_digest_authentication_test.rb | 23 +++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index b6b5267..eb64525 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -192,9 +192,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/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 00789ee..a725f7c 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -151,6 +151,16 @@ 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) @@ -161,15 +171,22 @@ class HttpDigestAuthenticationTest < ActionController::TestCase # to prevent tampering of timestamp ActionController::Base.session_options[:secret] = "session_options_secret" - # Perform unauthenticated GET to retrieve digest parameters to use on subsequent request - get :index + # Perform unauthenticated request to retrieve digest parameters to use on subsequent request + method = options.delete(:method) || 'GET' + + 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) -- 1.6.4.2 From 97b75c9f1668992bd246fe46235c5764a4fa6e58 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 18 May 2009 21:27:42 +0200 Subject: [PATCH 064/779] Make sure default_scope#create checks for options[:conditions] [#2181 state:resolved] [James Le Cuirot] --- activerecord/lib/active_record/base.rb | 2 +- activerecord/test/cases/method_scoping_test.rb | 10 ++++++++++ activerecord/test/models/developer.rb | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 545ee83..adfb7ad 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2163,7 +2163,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/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 9f23064..2f660a3 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/models/developer.rb b/activerecord/test/models/developer.rb index 92039a4..0589703 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 -- 1.6.4.2 From 7a85927da21859a6868c3e0ec92267706b0a14bf Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Mon, 16 Mar 2009 13:30:30 +0100 Subject: [PATCH 065/779] Ensure HasManyThroughAssociation#destroy delete orphan records [#2251 state:resolved] Signed-off-by: Pratik Naik --- .../associations/has_many_through_association.rb | 7 +++++++ .../has_many_through_associations_test.rb | 10 ++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) 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 1c091e7..efd96e3 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -22,6 +22,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/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 97efca7..51c70b9 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -93,7 +93,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase 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,6 +110,12 @@ 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.6.4.2 From 50608ecccdda7c3709c61484653f9ebb17068fcf Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 11 May 2009 23:22:20 -0400 Subject: [PATCH 066/779] Reimplement Fixtures.identify so that it consistently generates identities across ruby versions. [#2633 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/fixtures.rb | 9 +++++---- activerecord/test/cases/fixtures_test.rb | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index c650111..77f0931 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' @@ -433,6 +434,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 = {} @@ -524,11 +526,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/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 252bf4f..b07d4f3 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 -- 1.6.4.2 From ad85771221f6a05b5eb89601bd5336bd15b8e1f8 Mon Sep 17 00:00:00 2001 From: Joe Van Dyk Date: Mon, 2 Feb 2009 16:43:14 -0800 Subject: [PATCH 067/779] Add ability to get multiple memcached keys at the same time (via MemCacheStore#read_multi). Signed-off-by: Jeremy Kemper --- .../lib/active_support/cache/mem_cache_store.rb | 5 +++++ activesupport/test/caching_test.rb | 9 +++++++++ 2 files changed, 14 insertions(+), 0 deletions(-) create mode 100644 activesupport/memcached_get_multi.diff diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 4d8e1fd..90b7526 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -43,6 +43,11 @@ module ActiveSupport 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/memcached_get_multi.diff b/activesupport/memcached_get_multi.diff new file mode 100644 index 0000000..e69de29 diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 4e212f1..5b2527b 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -256,6 +256,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') -- 1.6.4.2 From 9fcadcbd68a474c265ab1bd1255082c432ef6821 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 18 May 2009 14:34:32 -0700 Subject: [PATCH 068/779] Fix imprecise float comparison --- activesupport/test/core_ext/duration_test.rb | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index ab5a866..3a71779 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -44,20 +44,20 @@ class DurationTest < ActiveSupport::TestCase Time.stubs(:now).returns Time.local(2000) # since assert_equal 36.hours.since, 1.5.days.since - assert_equal((24 * 1.7).hours.since, 1.7.days.since) + assert_in_delta((24 * 1.7).hours.since, 1.7.days.since, 0.01) # ago assert_equal 36.hours.ago, 1.5.days.ago - assert_equal((24 * 1.7).hours.ago, 1.7.days.ago) + assert_in_delta((24 * 1.7).hours.ago, 1.7.days.ago, 0.01) end def test_since_and_ago_with_fractional_weeks Time.stubs(:now).returns Time.local(2000) # since assert_equal((7 * 36).hours.since, 1.5.weeks.since) - assert_equal((7 * 24 * 1.7).hours.since, 1.7.weeks.since) + assert_in_delta((7 * 24 * 1.7).hours.since, 1.7.weeks.since, 0.01) # ago assert_equal((7 * 36).hours.ago, 1.5.weeks.ago) - assert_equal((7 * 24 * 1.7).hours.ago, 1.7.weeks.ago) + assert_in_delta((7 * 24 * 1.7).hours.ago, 1.7.weeks.ago, 0.01) end def test_deprecated_fractional_years -- 1.6.4.2 From 6339e5d360af1977788aca6f819762be6213772b Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Tue, 19 May 2009 10:24:26 -0400 Subject: [PATCH 069/779] Allow MemCacheStore to be initialized with a MemCache object instead of addresses and options --- activesupport/CHANGELOG | 5 +++++ .../lib/active_support/cache/mem_cache_store.rb | 17 +++++++++++------ activesupport/test/caching_test.rb | 14 ++++++++++---- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index ab40e1a..19ddf34 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.3 (pending)* + +* Allow MemCacheStore to be initialized with a MemCache object instead of addresses and options [Bryan Helmkamp] + + *2.3.2 [Final] (March 15, 2009)* * XmlMini supports LibXML and Nokogiri backends. #2084, #2190 [Bart ten Brinke, Aaron Patterson] diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 90b7526..ab8eb72 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,11 +39,11 @@ 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.is_a?(MemCache) + @data = addresses.first + else + @data = self.class.build_mem_cache(*addresses) + end extend Strategy::LocalCache end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 5b2527b..ff07232 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -20,22 +20,28 @@ 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_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 -- 1.6.4.2 From 2a657725f1a31c4d88675ea01affeaed315474ef Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 19 May 2009 10:59:24 -0700 Subject: [PATCH 070/779] Mark pending release in changelog instead of edge --- actionpack/CHANGELOG | 2 +- activerecord/CHANGELOG | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 11ee1c1..2e459fc 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,4 +1,4 @@ -*Edge* +*2.3.3 (pending)* * Fixed that TestResponse.cookies was returning cookies unescaped #1867 [Doug McInnes] diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index d58b441..c577706 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,4 @@ -*Edge* +*2.3.3 (pending)* * Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH] -- 1.6.4.2 From 542d6a0abd712be8892ef044ef40fe7bbc9631a9 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Tue, 19 May 2009 19:51:38 -0400 Subject: [PATCH 071/779] Use duck typing to also allow MemCache-like object when initializing a MemCacheStore Signed-off-by: Jeremy Kemper --- activesupport/CHANGELOG | 2 +- .../lib/active_support/cache/mem_cache_store.rb | 2 +- activesupport/test/caching_test.rb | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 19ddf34..d0e1ce9 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,6 +1,6 @@ *2.3.3 (pending)* -* Allow MemCacheStore to be initialized with a MemCache object instead of addresses and options [Bryan Helmkamp] +* Allow MemCacheStore to be initialized with a MemCache-like object instead of addresses and options [Bryan Helmkamp] *2.3.2 [Final] (March 15, 2009)* diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index ab8eb72..38b3409 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -39,7 +39,7 @@ module ActiveSupport # If no addresses are specified, then MemCacheStore will connect to # localhost port 11211 (the default memcached port). def initialize(*addresses) - if addresses.first.is_a?(MemCache) + if addresses.first.respond_to?(:get) @data = addresses.first else @data = self.class.build_mem_cache(*addresses) diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index ff07232..ede3e56 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -32,6 +32,12 @@ class CacheStoreSettingTest < ActiveSupport::TestCase 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' -- 1.6.4.2 From a70c78177a564c2f2cd09846a5e7ab6e8669e9f2 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Tue, 17 Mar 2009 01:04:47 +0100 Subject: [PATCH 072/779] Ensure the parent record is always saved when the child is invalid. [#2249 state:resolved] Signed-off-by: Pratik Naik --- .../lib/active_record/autosave_association.rb | 14 ++++++++---- .../test/cases/autosave_association_test.rb | 22 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 741aa2a..9717ca3 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -311,11 +311,13 @@ module ActiveRecord # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. def save_has_one_association(reflection) if (association = association_instance_get(reflection.name)) && !association.target.nil? - if reflection.options[:autosave] && association.marked_for_destruction? + autosave = reflection.options[:autosave] + + if autosave && association.marked_for_destruction? association.destroy - elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave] + elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave association[reflection.primary_key_name] = id - association.save(false) + association.save(!autosave) end end end @@ -330,10 +332,12 @@ module ActiveRecord # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. def save_belongs_to_association(reflection) if association = association_instance_get(reflection.name) - if reflection.options[:autosave] && association.marked_for_destruction? + autosave = reflection.options[:autosave] + + if autosave && association.marked_for_destruction? association.destroy else - association.save(false) if association.new_record? || reflection.options[:autosave] + association.save(!autosave) if association.new_record? || autosave if association.updated? self[reflection.primary_key_name] = association.id diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 436f50d..919b6f8 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -38,6 +38,17 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase end class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase + def test_should_save_parent_but_not_invalid_child + firm = Firm.new(:name => 'GlobalMegaCorp') + assert firm.valid? + + firm.build_account_using_primary_key + assert !firm.build_account_using_primary_key.valid? + + assert firm.save + assert firm.account_using_primary_key.new_record? + end + def test_save_fails_for_invalid_has_one firm = Firm.find(:first) assert firm.valid? @@ -126,6 +137,17 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas end class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase + def test_should_save_parent_but_not_invalid_child + client = Client.new(:name => 'Joe (the Plumber)') + assert client.valid? + + client.build_firm + assert !client.firm.valid? + + assert client.save + assert client.firm.new_record? + end + def test_save_fails_for_invalid_belongs_to assert log = AuditLog.create(:developer_id => 0, :message => "") -- 1.6.4.2 From 9b2a665aff321bf58311494bc946eecc8eba990c Mon Sep 17 00:00:00 2001 From: Hongli Lai (Phusion) Date: Sun, 24 May 2009 11:46:39 +0200 Subject: [PATCH 073/779] activesupport/json/encoders fix that to_json should call rails_to_json, not just be an alias to the rails_to_json method defined in Object. Fixes #2690 Signed-off-by: Pratik Naik --- .../lib/active_support/json/encoders/object.rb | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/json/encoders/object.rb b/activesupport/lib/active_support/json/encoders/object.rb index 79d556d..ba2d05f 100644 --- a/activesupport/lib/active_support/json/encoders/object.rb +++ b/activesupport/lib/active_support/json/encoders/object.rb @@ -4,5 +4,7 @@ class Object ActiveSupport::JSON.encode(instance_values, options) end - alias to_json rails_to_json + def to_json(*args) + rails_to_json(*args) + end end -- 1.6.4.2 From b4c7b3e893c586160ffe11aa598d729707f7f900 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 27 May 2009 14:54:58 -0500 Subject: [PATCH 074/779] Ensure Memcache local cache returns duplicated values [#2302 state:resolved] --- .../active_support/cache/strategy/local_cache.rb | 2 +- activesupport/test/caching_test.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index d83e259..ea59375 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -38,7 +38,7 @@ module ActiveSupport elsif value.nil? value = super local_cache.write(key, value || NULL) if local_cache - value + value.duplicable? ? value.dup : value else # forcing the value to be immutable value.duplicable? ? value.dup : value diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index ede3e56..81d0acf 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -185,6 +185,15 @@ uses_memcached 'memcached backed store' do end end + def test_stored_objects_should_not_be_frozen + @cache.with_local_cache do + @cache.write('foo', 'bar') + end + @cache.with_local_cache do + assert !@cache.read('foo').frozen? + end + end + def test_write_should_return_true_on_success @cache.with_local_cache do result = @cache.write('foo', 'bar') -- 1.6.4.2 From 4196616778846f5ccbd8da504a2306846fd3f432 Mon Sep 17 00:00:00 2001 From: calavera Date: Wed, 27 May 2009 14:56:14 -0500 Subject: [PATCH 075/779] ensure initialize_database_middleware doesn't use ActionController if action_controller framework is not enabled [#2680 state:resolved] Signed-off-by: Joshua Peek --- railties/lib/initializer.rb | 6 ++++-- railties/test/initializer_test.rb | 23 +++++++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 4d34b82..9ffb00f 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -441,7 +441,8 @@ Run `rake gems:install` to install the missing gems. def initialize_database_middleware if configuration.frameworks.include?(:active_record) - if ActionController::Base.session_store == ActiveRecord::SessionStore + if configuration.frameworks.include?(:action_controller) && + ActionController::Base.session_store == ActiveRecord::SessionStore configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::QueryCache else @@ -883,7 +884,7 @@ Run `rake gems:install` to install the missing gems. # Enable threaded mode. Allows concurrent requests to controller actions and # multiple database connections. Also disables automatic dependency loading - # after boot, and disables reloading code on every request, as these are + # after boot, and disables reloading code on every request, as these are # fundamentally incompatible with thread safety. def threadsafe! self.preload_frameworks = true @@ -1124,3 +1125,4 @@ class Rails::OrderedOptions < Array #:nodoc: return false end end + diff --git a/railties/test/initializer_test.rb b/railties/test/initializer_test.rb index 561f7b8..ace0cca 100644 --- a/railties/test/initializer_test.rb +++ b/railties/test/initializer_test.rb @@ -309,7 +309,7 @@ class InitializerSetupI18nTests < Test::Unit::TestCase config.i18n.load_path << "my/other/locale.yml" Rails::Initializer.run(:initialize_i18n, config) - assert_equal [ + assert_equal [ File.expand_path(File.dirname(__FILE__) + "/../../activesupport/lib/active_support/locale/en.yml"), File.expand_path(File.dirname(__FILE__) + "/../../actionpack/lib/action_view/locale/en.yml"), File.expand_path(File.dirname(__FILE__) + "/../../activerecord/lib/active_record/locale/en.yml"), @@ -363,17 +363,31 @@ class InitializerDatabaseMiddlewareTest < Test::Unit::TestCase ensure ActionController::Base.session_store = store end + + def test_ensure_database_middleware_doesnt_use_action_controller_on_initializing + @config.frameworks -= [:action_controller] + store = ActionController::Base.session_store + ActionController::Base.session_store = ActiveRecord::SessionStore + + @config.middleware.expects(:use).with(ActiveRecord::ConnectionAdapters::ConnectionManagement) + @config.middleware.expects(:use).with(ActiveRecord::QueryCache) + + Rails::Initializer.run(:initialize_database_middleware, @config) + ensure + ActionController::Base.session_store = store + @config.frameworks += [:action_controller] + end end class InitializerViewPathsTest < Test::Unit::TestCase def setup @config = Rails::Configuration.new @config.frameworks = [:action_view, :action_controller, :action_mailer] - + ActionController::Base.stubs(:view_paths).returns(stub) ActionMailer::Base.stubs(:view_paths).returns(stub) end - + def test_load_view_paths_doesnt_perform_anything_when_action_view_not_in_frameworks @config.frameworks -= [:action_view] ActionController::Base.view_paths.expects(:load!).never @@ -396,4 +410,5 @@ class RailsRootTest < Test::Unit::TestCase def test_rails_dot_root_should_be_a_pathname assert_equal File.join(RAILS_ROOT, 'app', 'controllers'), Rails.root.join('app', 'controllers').to_s end -end \ No newline at end of file +end + -- 1.6.4.2 From 34a1ed0df8311aba350043ec7ef373da3058855f Mon Sep 17 00:00:00 2001 From: Hongli Lai (Phusion) Date: Wed, 27 May 2009 14:59:11 -0500 Subject: [PATCH 076/779] Make the Failsafe middleware attempt to render 500.html during failsafe response rendering. Also make the default static failsafe response more friendly, in case 500.html rendering fails. [#2715 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/failsafe.rb | 42 ++++++++++++++++-- actionpack/test/controller/dispatcher_test.rb | 11 +++-- actionpack/test/controller/failsafe_test.rb | 60 +++++++++++++++++++++++++ actionpack/test/fixtures/failsafe/500.html | 1 + 4 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 actionpack/test/controller/failsafe_test.rb create mode 100644 actionpack/test/fixtures/failsafe/500.html diff --git a/actionpack/lib/action_controller/failsafe.rb b/actionpack/lib/action_controller/failsafe.rb index 36d0ab5..d242585 100644 --- a/actionpack/lib/action_controller/failsafe.rb +++ b/actionpack/lib/action_controller/failsafe.rb @@ -1,4 +1,19 @@ +require 'erb' + module ActionController + # The Failsafe middleware is usually the top-most middleware in the Rack + # middleware chain. It returns the underlying middleware's response, but if + # the underlying middle raises an exception then Failsafe will log the + # exception into the Rails log file, and will attempt to return an error + # message response. + # + # Failsafe is a last resort for logging errors and for telling the HTTP + # client that something went wrong. Do not confuse this with the + # ActionController::Rescue module, which is responsible for catching + # exceptions at deeper levels. Unlike Failsafe, which is as simple as + # possible, Rescue provides features that allow developers to hook into + # the error handling logic, and can customize the error message response + # based on the HTTP client's IP. class Failsafe cattr_accessor :error_file_path self.error_file_path = Rails.public_path if defined?(Rails.public_path) @@ -27,12 +42,31 @@ module ActionController end def failsafe_response_body - error_path = "#{self.class.error_file_path}/500.html" - if File.exist?(error_path) - File.read(error_path) + error_template_path = "#{self.class.error_file_path}/500.html" + if File.exist?(error_template_path) + begin + result = render_template(error_template_path) + rescue Exception + result = nil + end else - "

500 Internal Server Error

" + result = nil + end + if result.nil? + result = "

500 Internal Server Error

" << + "If you are the administrator of this website, then please read this web " << + "application's log file to find out what went wrong." end + result + end + + # The default 500.html uses the h() method. + def h(text) # :nodoc: + ERB::Util.h(text) + end + + def render_template(filename) + ERB.new(File.read(filename)).result(binding) end def log_failsafe_exception(exception) diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index 1e6243f..070a43e 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -49,13 +49,14 @@ class DispatcherTest < Test::Unit::TestCase Dispatcher.any_instance.expects(:dispatch).raises('b00m') ActionController::Failsafe.any_instance.expects(:log_failsafe_exception) + response = nil assert_nothing_raised do - assert_equal [ - 500, - {"Content-Type" => "text/html"}, - "

500 Internal Server Error

" - ], dispatch + response = dispatch end + assert_equal 3, response.size + assert_equal 500, response[0] + assert_equal({"Content-Type" => "text/html"}, response[1]) + assert_match /500 Internal Server Error/, response[2] end def test_prepare_callbacks diff --git a/actionpack/test/controller/failsafe_test.rb b/actionpack/test/controller/failsafe_test.rb new file mode 100644 index 0000000..2593e3a --- /dev/null +++ b/actionpack/test/controller/failsafe_test.rb @@ -0,0 +1,60 @@ +require 'abstract_unit' +require 'stringio' +require 'logger' + +class FailsafeTest < ActionController::TestCase + FIXTURE_PUBLIC = "#{File.dirname(__FILE__)}/../fixtures/failsafe".freeze + + def setup + @old_error_file_path = ActionController::Failsafe.error_file_path + ActionController::Failsafe.error_file_path = FIXTURE_PUBLIC + @app = mock + @log_io = StringIO.new + @logger = Logger.new(@log_io) + @failsafe = ActionController::Failsafe.new(@app) + @failsafe.stubs(:failsafe_logger).returns(@logger) + end + + def teardown + ActionController::Failsafe.error_file_path = @old_error_file_path + end + + def app_will_raise_error! + @app.expects(:call).then.raises(RuntimeError.new("Printer on fire")) + end + + def test_calls_app_and_returns_its_return_value + @app.expects(:call).returns([200, { "Content-Type" => "text/html" }, "ok"]) + assert_equal [200, { "Content-Type" => "text/html" }, "ok"], @failsafe.call({}) + end + + def test_writes_to_log_file_on_exception + app_will_raise_error! + @failsafe.call({}) + assert_match /Printer on fire/, @log_io.string # Logs exception message. + assert_match /failsafe_test\.rb/, @log_io.string # Logs backtrace. + end + + def test_returns_500_internal_server_error_on_exception + app_will_raise_error! + response = @failsafe.call({}) + assert_equal 3, response.size # It is a valid Rack response. + assert_equal 500, response[0] # Status is 500. + end + + def test_renders_error_page_file_with_erb + app_will_raise_error! + response = @failsafe.call({}) + assert_equal 500, response[0] + assert_equal "hello my world", response[2] + end + + def test_returns_a_default_message_if_erb_rendering_failed + app_will_raise_error! + @failsafe.expects(:render_template).raises(RuntimeError.new("Harddisk is crashing")) + response = @failsafe.call({}) + assert_equal 500, response[0] + assert_match /500 Internal Server Error/, response[2] + assert_match %r(please read this web application's log file), response[2] + end +end \ No newline at end of file diff --git a/actionpack/test/fixtures/failsafe/500.html b/actionpack/test/fixtures/failsafe/500.html new file mode 100644 index 0000000..f3af84d --- /dev/null +++ b/actionpack/test/fixtures/failsafe/500.html @@ -0,0 +1 @@ +hello <%= "my" %> world \ No newline at end of file -- 1.6.4.2 From dc94c095031036ffc0f9ac0ee98a1162e57062eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20S=C3=B6rensen?= Date: Thu, 28 May 2009 09:22:35 -0500 Subject: [PATCH 077/779] The FlashHash and friends causes a lot of needless session storing, when we know for a fact that there's no content in the flash. By not storing the empty hash in the session we save a lot of communication with the various session backends, while still keeping the same interface to the flash. [#2703 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/flash.rb | 14 +++++++++++--- actionpack/test/controller/flash_test.rb | 7 ++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_controller/flash.rb b/actionpack/lib/action_controller/flash.rb index 56ee9c6..9391207 100644 --- a/actionpack/lib/action_controller/flash.rb +++ b/actionpack/lib/action_controller/flash.rb @@ -120,6 +120,11 @@ module ActionController #:nodoc: (@used.keys - keys).each{ |k| @used.delete(k) } end + def store(session, key = "flash") + return if self.empty? + session[key] = self + end + private # Used internally by the keep and discard methods # use() # marks the entire flash as used @@ -139,7 +144,10 @@ module ActionController #:nodoc: protected def perform_action_with_flash perform_action_without_flash - remove_instance_variable(:@_flash) if defined? @_flash + if defined? @_flash + @_flash.store(session) + remove_instance_variable(:@_flash) + end end def reset_session_with_flash @@ -151,8 +159,8 @@ module ActionController #:nodoc: # read a notice you put there or flash["notice"] = "hello" # to put a new one. def flash #:doc: - unless defined? @_flash - @_flash = session["flash"] ||= FlashHash.new + if !defined?(@_flash) + @_flash = session["flash"] || FlashHash.new @_flash.sweep end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index d8a8928..81c724a 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -121,7 +121,7 @@ class FlashTest < ActionController::TestCase assert_nil @response.template.assigns["flash_copy"]["that"], "On second flash" assert_equal "hello again", @response.template.assigns["flash_copy"]["this"], "On second flash" end - + def test_flash_after_reset_session get :use_flash_after_reset_session assert_equal "hello", @response.template.assigns["flashy_that"] @@ -139,4 +139,9 @@ class FlashTest < ActionController::TestCase get :std_action assert_nil @response.template.assigns["flash_copy"]["foo"] end + + def test_does_not_set_the_session_if_the_flash_is_empty + get :std_action + assert_nil session["flash"] + end end -- 1.6.4.2 From 14edaa104de7b71a4cd11b03121c911ce221b813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20S=C3=B6rensen?= Date: Thu, 28 May 2009 09:32:16 -0500 Subject: [PATCH 078/779] Only save the session if we're actually writing to it [#2703 state:resolved] Signed-off-by: Joshua Peek --- .../action_controller/session/abstract_store.rb | 11 +++++++++- .../test/activerecord/active_record_store_test.rb | 22 ++++++++++++++++++++ .../controller/session/mem_cache_store_test.rb | 8 +++++++ 3 files changed, 40 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/session/abstract_store.rb b/actionpack/lib/action_controller/session/abstract_store.rb index f6369ab..16a9127 100644 --- a/actionpack/lib/action_controller/session/abstract_store.rb +++ b/actionpack/lib/action_controller/session/abstract_store.rb @@ -15,6 +15,7 @@ module ActionController @by = by @env = env @loaded = false + @updated = false end def session_id @@ -32,6 +33,7 @@ module ActionController def []=(key, value) load! unless @loaded super + @updated = true end def to_hash @@ -57,6 +59,10 @@ module ActionController @loaded end + def updated? + @updated + end + def load! stale_session_check! do id, session = @by.send(:load_session, @env) @@ -125,7 +131,10 @@ module ActionController options = env[ENV_SESSION_OPTIONS_KEY] if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after] - session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?) + if session_data.is_a?(AbstractStore::SessionHash) + session_data.send(:load!) if !session_data.send(:loaded?) + return response if !session_data.send(:updated?) + end sid = options[:id] || generate_sid diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb index bde36eb..14c6fc1 100644 --- a/actionpack/test/activerecord/active_record_store_test.rb +++ b/actionpack/test/activerecord/active_record_store_test.rb @@ -21,6 +21,11 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest render :text => "foo: #{session[:foo].inspect}" end + def set_cookie_and_get_session_value + cookies["kittens"] = { :value => "fluffy" } + render :text => "foo: #{session[:foo].inspect}" + end + def get_session_id session[:foo] render :text => "#{request.session_options[:id]}" @@ -73,6 +78,23 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest end end + def test_getting_session_value_does_not_set_cookie + with_test_route_set do + get '/get_session_value' + assert_response :success + assert_equal "", headers["Set-Cookie"] + end + end + + def test_getting_session_value_and_setting_a_cookie_doesnt_delete_all_cookies + with_test_route_set do + get '/set_cookie_and_get_session_value' + assert_response :success + assert_equal 'foo: nil', response.body + assert_equal({"kittens" => "fluffy"}, response.cookies) + end + end + def test_setting_session_value_after_session_reset with_test_route_set do get '/set_session_value' diff --git a/actionpack/test/controller/session/mem_cache_store_test.rb b/actionpack/test/controller/session/mem_cache_store_test.rb index 2f80a3c..c7988f5 100644 --- a/actionpack/test/controller/session/mem_cache_store_test.rb +++ b/actionpack/test/controller/session/mem_cache_store_test.rb @@ -61,6 +61,14 @@ class MemCacheStoreTest < ActionController::IntegrationTest end end + def test_getting_session_value_does_not_set_cookie + with_test_route_set do + get '/get_session_value' + assert_response :success + assert_equal "", headers["Set-Cookie"] + end + end + def test_setting_session_value_after_session_reset with_test_route_set do get '/set_session_value' -- 1.6.4.2 From c73cf7d2c0d164d819a483c83302cd4a62ca24b9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 May 2009 09:36:32 -0500 Subject: [PATCH 079/779] Revert "Only save the session if we're actually writing to it [#2703 state:resolved]" This reverts commit 14edaa104de7b71a4cd11b03121c911ce221b813. --- .../action_controller/session/abstract_store.rb | 11 +--------- .../test/activerecord/active_record_store_test.rb | 22 -------------------- .../controller/session/mem_cache_store_test.rb | 8 ------- 3 files changed, 1 insertions(+), 40 deletions(-) diff --git a/actionpack/lib/action_controller/session/abstract_store.rb b/actionpack/lib/action_controller/session/abstract_store.rb index 16a9127..f6369ab 100644 --- a/actionpack/lib/action_controller/session/abstract_store.rb +++ b/actionpack/lib/action_controller/session/abstract_store.rb @@ -15,7 +15,6 @@ module ActionController @by = by @env = env @loaded = false - @updated = false end def session_id @@ -33,7 +32,6 @@ module ActionController def []=(key, value) load! unless @loaded super - @updated = true end def to_hash @@ -59,10 +57,6 @@ module ActionController @loaded end - def updated? - @updated - end - def load! stale_session_check! do id, session = @by.send(:load_session, @env) @@ -131,10 +125,7 @@ module ActionController options = env[ENV_SESSION_OPTIONS_KEY] if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after] - if session_data.is_a?(AbstractStore::SessionHash) - session_data.send(:load!) if !session_data.send(:loaded?) - return response if !session_data.send(:updated?) - end + session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?) sid = options[:id] || generate_sid diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb index 14c6fc1..bde36eb 100644 --- a/actionpack/test/activerecord/active_record_store_test.rb +++ b/actionpack/test/activerecord/active_record_store_test.rb @@ -21,11 +21,6 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest render :text => "foo: #{session[:foo].inspect}" end - def set_cookie_and_get_session_value - cookies["kittens"] = { :value => "fluffy" } - render :text => "foo: #{session[:foo].inspect}" - end - def get_session_id session[:foo] render :text => "#{request.session_options[:id]}" @@ -78,23 +73,6 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest end end - def test_getting_session_value_does_not_set_cookie - with_test_route_set do - get '/get_session_value' - assert_response :success - assert_equal "", headers["Set-Cookie"] - end - end - - def test_getting_session_value_and_setting_a_cookie_doesnt_delete_all_cookies - with_test_route_set do - get '/set_cookie_and_get_session_value' - assert_response :success - assert_equal 'foo: nil', response.body - assert_equal({"kittens" => "fluffy"}, response.cookies) - end - end - def test_setting_session_value_after_session_reset with_test_route_set do get '/set_session_value' diff --git a/actionpack/test/controller/session/mem_cache_store_test.rb b/actionpack/test/controller/session/mem_cache_store_test.rb index c7988f5..2f80a3c 100644 --- a/actionpack/test/controller/session/mem_cache_store_test.rb +++ b/actionpack/test/controller/session/mem_cache_store_test.rb @@ -61,14 +61,6 @@ class MemCacheStoreTest < ActionController::IntegrationTest end end - def test_getting_session_value_does_not_set_cookie - with_test_route_set do - get '/get_session_value' - assert_response :success - assert_equal "", headers["Set-Cookie"] - end - end - def test_setting_session_value_after_session_reset with_test_route_set do get '/set_session_value' -- 1.6.4.2 From 34c3162c5c84a4393340e7fc5f7f7f37a828fc08 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Mon, 1 Jun 2009 13:54:20 +1200 Subject: [PATCH 080/779] Revert "Ensure calculations respect scoped :select". Broke .count on a has_many :through association. This reverts commit 6543426c73fa9ccf3649d7cbacbbb0fda9b6a099. --- activerecord/lib/active_record/calculations.rb | 18 +++++------------- activerecord/test/cases/calculations_test.rb | 13 ------------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index f077818..7af97d7 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -141,30 +141,22 @@ module ActiveRecord def construct_count_options_from_args(*args) options = {} column_name = :all - + # We need to handle # count() # count(:column_name=:all) # count(options={}) # count(column_name=:all, options={}) - # selects specified by scopes case args.size - when 0 - column_name = scope(:find)[:select] if scope(:find) when 1 - if args[0].is_a?(Hash) - column_name = scope(:find)[:select] if scope(:find) - options = args[0] - else - column_name = args[0] - end + args[0].is_a?(Hash) ? options = args[0] : column_name = args[0] when 2 column_name, options = args else raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}" - end - - [column_name || :all, options] + end if args.size > 0 + + [column_name, options] end def construct_calculation_sql(operation, column_name, options) #:nodoc: diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 56dcdea..efad7d8 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -264,19 +264,6 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal 4, Account.count(:distinct => true, :include => :firm, :select => :credit_limit) end - def test_should_count_scoped_select - Account.update_all("credit_limit = NULL") - assert_equal 0, Account.scoped(:select => "credit_limit").count - end - - def test_should_count_scoped_select_with_options - Account.update_all("credit_limit = NULL") - Account.last.update_attribute('credit_limit', 49) - Account.first.update_attribute('credit_limit', 51) - - assert_equal 1, Account.scoped(:select => "credit_limit").count(:conditions => ['credit_limit >= 50']) - end - def test_should_count_manual_select_with_include assert_equal 6, Account.count(:select => "DISTINCT accounts.id", :include => :firm) end -- 1.6.4.2 From a92790ab862790acc627a3194770da37e400d64b Mon Sep 17 00:00:00 2001 From: Ian Terrell Date: Tue, 17 Mar 2009 12:08:47 -0400 Subject: [PATCH 081/779] added a failing test case for counting has_many :through associations with scopes Signed-off-by: Michael Koziarski --- activerecord/test/cases/calculations_test.rb | 9 ++++++++- activerecord/test/models/toy.rb | 2 ++ 2 files changed, 10 insertions(+), 1 deletions(-) diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index efad7d8..f690e28 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -2,6 +2,9 @@ require "cases/helper" require 'models/company' require 'models/topic' require 'models/edge' +require 'models/owner' +require 'models/pet' +require 'models/toy' Company.has_many :accounts @@ -10,7 +13,7 @@ class NumericData < ActiveRecord::Base end class CalculationsTest < ActiveRecord::TestCase - fixtures :companies, :accounts, :topics + fixtures :companies, :accounts, :topics, :owners, :pets, :toys def test_should_sum_field assert_equal 318, Account.sum(:credit_limit) @@ -284,6 +287,10 @@ class CalculationsTest < ActiveRecord::TestCase assert_raise(ArgumentError) { Account.count(1, 2, 3) } end + def test_count_with_scoped_has_many_through_association + assert_equal 1, owners(:blackbeard).toys.with_name('bone').count + end + def test_should_sum_expression assert_equal '636', Account.sum("2 * credit_limit") end diff --git a/activerecord/test/models/toy.rb b/activerecord/test/models/toy.rb index 79a88db..0e68ba5 100644 --- a/activerecord/test/models/toy.rb +++ b/activerecord/test/models/toy.rb @@ -1,4 +1,6 @@ class Toy < ActiveRecord::Base set_primary_key :toy_id belongs_to :pet + + named_scope :with_name, lambda { |name| {:conditions => {:name => name}} } end -- 1.6.4.2 From 4d7c597e84c807a082508b6d1a105a08e34f10e0 Mon Sep 17 00:00:00 2001 From: Han Kessels Date: Fri, 27 Mar 2009 15:25:27 +0900 Subject: [PATCH 082/779] fix for IE incompatibility of :disable_with in submit_tag Signed-off-by: Michael Koziarski --- .../lib/action_view/helpers/form_tag_helper.rb | 3 ++- actionpack/test/template/form_tag_helper_test.rb | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 6d39a53..f5f26f1 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -353,7 +353,8 @@ module ActionView disable_with << ";#{options.delete('onclick')}" if options['onclick'] options["onclick"] = "if (window.hiddenCommit) { window.hiddenCommit.setAttribute('value', this.value); }" - options["onclick"] << "else { hiddenCommit = this.cloneNode(false);hiddenCommit.setAttribute('type', 'hidden');this.form.appendChild(hiddenCommit); }" + options["onclick"] << "else { hiddenCommit = document.createElement('input');hiddenCommit.type = 'hidden';" + options["onclick"] << "hiddenCommit.value = this.value;hiddenCommit.name = this.name;this.form.appendChild(hiddenCommit); }" options["onclick"] << "this.setAttribute('originalValue', this.value);this.disabled = true;#{disable_with};" options["onclick"] << "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());" options["onclick"] << "if (result == false) { this.value = this.getAttribute('originalValue');this.disabled = false; }return result;" diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index c713b8d..e3f64cd 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -252,14 +252,14 @@ class FormTagHelperTest < ActionView::TestCase def test_submit_tag assert_dom_equal( - %(), + %(), submit_tag("Save", :disable_with => "Saving...", :onclick => "alert('hello!')") ) end def test_submit_tag_with_no_onclick_options assert_dom_equal( - %(), + %(), submit_tag("Save", :disable_with => "Saving...") ) end @@ -273,7 +273,7 @@ class FormTagHelperTest < ActionView::TestCase def test_submit_tag_with_confirmation_and_with_disable_with assert_dom_equal( - %(), + %(), submit_tag("Save", :disable_with => "Saving...", :confirm => "Are you sure?") ) end -- 1.6.4.2 From b600bf2cd728c90d50cc34456c944b2dfefe8c8d Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Mon, 1 Jun 2009 14:21:08 +1200 Subject: [PATCH 083/779] name is case sensitive, update tests to reflect that --- activerecord/test/cases/calculations_test.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index f690e28..b4f76cb 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -288,7 +288,7 @@ class CalculationsTest < ActiveRecord::TestCase end def test_count_with_scoped_has_many_through_association - assert_equal 1, owners(:blackbeard).toys.with_name('bone').count + assert_equal 1, owners(:blackbeard).toys.with_name('Bone').count end def test_should_sum_expression -- 1.6.4.2 From 84a755b27e615da4a51b6e4c042491b6d807bcf8 Mon Sep 17 00:00:00 2001 From: Tim Connor Date: Thu, 21 May 2009 14:51:34 -0700 Subject: [PATCH 084/779] Work around a gem dependency edge case that prevents Rails from booting.. If you have a frozen gem with unfrozen dependencies (for instance if the dependency has native extensions so can't be frozen) you can have a nightmare upgrade problem, where you cannot rake gems:install, because rake is broken by a gem loading problem. If you bump up your frozen gem to a newer version that requires a newer dependency, everybody else on the team will have rake broken by that dependency mismatch, since you will have had to specify the dependency in your config.gems, otherwise nobody will have installed it, since the parent is frozen. And now the config.gems loading code will kill rake. [#2609 state:committed] Signed-off-by: Jeremy Kemper --- railties/lib/initializer.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 9ffb00f..5f5e557 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -303,7 +303,7 @@ module Rails end def load_gems - unless $gems_build_rake_task + unless $gems_rake_task @configuration.gems.each { |gem| gem.load } end end -- 1.6.4.2 From 4b4164e8a840970cfa558e0e7bf59ee1948b6506 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 00:09:50 -0700 Subject: [PATCH 085/779] Don't rely on Rails.logger --- .../lib/active_support/core_ext/kernel/debugger.rb | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/kernel/debugger.rb b/activesupport/lib/active_support/core_ext/kernel/debugger.rb index 4007a64..0813a51 100644 --- a/activesupport/lib/active_support/core_ext/kernel/debugger.rb +++ b/activesupport/lib/active_support/core_ext/kernel/debugger.rb @@ -2,12 +2,14 @@ module Kernel unless respond_to?(:debugger) # Starts a debugging session if ruby-debug has been loaded (call script/server --debugger to do load it). def debugger - Rails.logger.info "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n" + message = "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n" + defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message) end end def breakpoint - Rails.logger.info "\n***** The 'breakpoint' command has been renamed 'debugger' -- please change *****\n" + message = "\n***** The 'breakpoint' command has been renamed 'debugger' -- please change *****\n" + defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message) debugger end end -- 1.6.4.2 From 4a78dae2ab43b5ba94fa06b345e9edeb0305fe44 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 00:11:06 -0700 Subject: [PATCH 086/779] Revert rails_to_json -> to_json so we don't break compatibility [#2753 state:resolved] --- .../lib/active_support/json/encoders/date.rb | 2 +- .../lib/active_support/json/encoders/date_time.rb | 2 +- .../lib/active_support/json/encoders/enumerable.rb | 8 +++++++- .../active_support/json/encoders/false_class.rb | 2 +- .../lib/active_support/json/encoders/hash.rb | 2 +- .../lib/active_support/json/encoders/nil_class.rb | 2 +- .../lib/active_support/json/encoders/numeric.rb | 14 +++++++++++++- .../lib/active_support/json/encoders/object.rb | 6 +----- .../lib/active_support/json/encoders/regexp.rb | 2 +- .../lib/active_support/json/encoders/string.rb | 2 +- .../lib/active_support/json/encoders/symbol.rb | 2 +- .../lib/active_support/json/encoders/time.rb | 2 +- .../lib/active_support/json/encoders/true_class.rb | 2 +- activesupport/lib/active_support/json/encoding.rb | 10 ++++++++-- activesupport/lib/active_support/json/variable.rb | 4 +--- activesupport/lib/active_support/time_with_zone.rb | 4 +--- activesupport/test/json/encoding_test.rb | 19 ++++++++++++++++++- 17 files changed, 59 insertions(+), 26 deletions(-) diff --git a/activesupport/lib/active_support/json/encoders/date.rb b/activesupport/lib/active_support/json/encoders/date.rb index 1aebdd2..cc84de1 100644 --- a/activesupport/lib/active_support/json/encoders/date.rb +++ b/activesupport/lib/active_support/json/encoders/date.rb @@ -11,7 +11,7 @@ class Date # # With ActiveSupport.use_standard_json_time_format = false # Date.new(2005,2,1).to_json # # => "2005/02/01" - def rails_to_json(options = nil) + def to_json(options = nil) if ActiveSupport.use_standard_json_time_format %("#{strftime("%Y-%m-%d")}") else diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb index 096e0dd..6c85824 100644 --- a/activesupport/lib/active_support/json/encoders/date_time.rb +++ b/activesupport/lib/active_support/json/encoders/date_time.rb @@ -11,7 +11,7 @@ class DateTime # # With ActiveSupport.use_standard_json_time_format = false # DateTime.civil(2005,2,1,15,15,10).to_json # # => "2005/02/01 15:15:10 +0000" - def rails_to_json(options = nil) + def to_json(options = nil) if ActiveSupport.use_standard_json_time_format xmlschema.inspect else diff --git a/activesupport/lib/active_support/json/encoders/enumerable.rb b/activesupport/lib/active_support/json/encoders/enumerable.rb index 6e0c3b6..7de7ea4 100644 --- a/activesupport/lib/active_support/json/encoders/enumerable.rb +++ b/activesupport/lib/active_support/json/encoders/enumerable.rb @@ -6,7 +6,13 @@ module Enumerable # # => users.to_json(:only => :name) # # will pass the :only => :name option to each user. - def rails_to_json(options = nil) #:nodoc: + def to_json(options = nil) #:nodoc: + to_a.to_json(options) + end +end + +class Array + def to_json(options = nil) #:nodoc: "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ','}]" end end diff --git a/activesupport/lib/active_support/json/encoders/false_class.rb b/activesupport/lib/active_support/json/encoders/false_class.rb index 4c47b33..bf08443 100644 --- a/activesupport/lib/active_support/json/encoders/false_class.rb +++ b/activesupport/lib/active_support/json/encoders/false_class.rb @@ -1,5 +1,5 @@ class FalseClass - def rails_to_json(options = nil) #:nodoc: + def to_json(options = nil) #:nodoc: 'false' end end diff --git a/activesupport/lib/active_support/json/encoders/hash.rb b/activesupport/lib/active_support/json/encoders/hash.rb index 6991163..48ee96d 100644 --- a/activesupport/lib/active_support/json/encoders/hash.rb +++ b/activesupport/lib/active_support/json/encoders/hash.rb @@ -28,7 +28,7 @@ class Hash # would pass the :include => :posts option to users, # allowing the posts association in the User model to be converted to JSON # as well. - def rails_to_json(options = nil) #:nodoc: + def to_json(options = nil) #:nodoc: hash_keys = self.keys if options diff --git a/activesupport/lib/active_support/json/encoders/nil_class.rb b/activesupport/lib/active_support/json/encoders/nil_class.rb index b7b63e2..4763471 100644 --- a/activesupport/lib/active_support/json/encoders/nil_class.rb +++ b/activesupport/lib/active_support/json/encoders/nil_class.rb @@ -1,5 +1,5 @@ class NilClass - def rails_to_json(options = nil) #:nodoc: + def to_json(options = nil) #:nodoc: 'null' end end diff --git a/activesupport/lib/active_support/json/encoders/numeric.rb b/activesupport/lib/active_support/json/encoders/numeric.rb index b969902..022af18 100644 --- a/activesupport/lib/active_support/json/encoders/numeric.rb +++ b/activesupport/lib/active_support/json/encoders/numeric.rb @@ -1,5 +1,17 @@ class Numeric - def rails_to_json(options = nil) #:nodoc: + def to_json(options = nil) #:nodoc: + to_s + end +end + +class Float + def to_json(options = nil) #:nodoc: + to_s + end +end + +class Integer + def to_json(options = nil) #:nodoc: to_s end end diff --git a/activesupport/lib/active_support/json/encoders/object.rb b/activesupport/lib/active_support/json/encoders/object.rb index ba2d05f..9c40ebc 100644 --- a/activesupport/lib/active_support/json/encoders/object.rb +++ b/activesupport/lib/active_support/json/encoders/object.rb @@ -1,10 +1,6 @@ class Object # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. - def rails_to_json(options = nil) + def to_json(options = nil) ActiveSupport::JSON.encode(instance_values, options) end - - def to_json(*args) - rails_to_json(*args) - end end diff --git a/activesupport/lib/active_support/json/encoders/regexp.rb b/activesupport/lib/active_support/json/encoders/regexp.rb index ff7dd67..b6116b7 100644 --- a/activesupport/lib/active_support/json/encoders/regexp.rb +++ b/activesupport/lib/active_support/json/encoders/regexp.rb @@ -1,5 +1,5 @@ class Regexp - def rails_to_json(options = nil) #:nodoc: + def to_json(options = nil) #:nodoc: inspect end end diff --git a/activesupport/lib/active_support/json/encoders/string.rb b/activesupport/lib/active_support/json/encoders/string.rb index ea82ca2..56d869a 100644 --- a/activesupport/lib/active_support/json/encoders/string.rb +++ b/activesupport/lib/active_support/json/encoders/string.rb @@ -1,5 +1,5 @@ class String - def rails_to_json(options = nil) #:nodoc: + def to_json(options = nil) #:nodoc: ActiveSupport::JSON::Encoding.escape(self) end end diff --git a/activesupport/lib/active_support/json/encoders/symbol.rb b/activesupport/lib/active_support/json/encoders/symbol.rb index c39eda8..00d45b6 100644 --- a/activesupport/lib/active_support/json/encoders/symbol.rb +++ b/activesupport/lib/active_support/json/encoders/symbol.rb @@ -1,5 +1,5 @@ class Symbol - def rails_to_json(options = nil) #:nodoc: + def to_json(options = nil) #:nodoc: ActiveSupport::JSON.encode(to_s, options) end end diff --git a/activesupport/lib/active_support/json/encoders/time.rb b/activesupport/lib/active_support/json/encoders/time.rb index bd0062e..f45a005 100644 --- a/activesupport/lib/active_support/json/encoders/time.rb +++ b/activesupport/lib/active_support/json/encoders/time.rb @@ -11,7 +11,7 @@ class Time # # With ActiveSupport.use_standard_json_time_format = false # Time.utc(2005,2,1,15,15,10).to_json # # => "2005/02/01 15:15:10 +0000" - def rails_to_json(options = nil) + def to_json(options = nil) if ActiveSupport.use_standard_json_time_format xmlschema.inspect else diff --git a/activesupport/lib/active_support/json/encoders/true_class.rb b/activesupport/lib/active_support/json/encoders/true_class.rb index a22c487..037d812 100644 --- a/activesupport/lib/active_support/json/encoders/true_class.rb +++ b/activesupport/lib/active_support/json/encoders/true_class.rb @@ -1,5 +1,5 @@ class TrueClass - def rails_to_json(options = nil) #:nodoc: + def to_json(options = nil) #:nodoc: 'true' end end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 76f8926..3999e1c 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,3 +1,9 @@ +# Hack to load json gem first so we can overwrite its to_json. +begin + require 'json' +rescue LoadError +end + module ActiveSupport module JSON class CircularReferenceError < StandardError @@ -5,11 +11,11 @@ module ActiveSupport # Converts a Ruby object into a JSON string. def self.encode(value, options = nil) - options ||= {} + options = {} unless Hash === options seen = (options[:seen] ||= []) raise CircularReferenceError, 'object references itself' if seen.include?(value) seen << value - value.rails_to_json(options) + value.to_json(options) ensure seen.pop end diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb index 9924168..7fd23b0 100644 --- a/activesupport/lib/active_support/json/variable.rb +++ b/activesupport/lib/active_support/json/variable.rb @@ -2,11 +2,9 @@ module ActiveSupport module JSON # A string that returns itself as its JSON-encoded form. class Variable < String - def rails_to_json(options=nil) + def to_json(options=nil) self end - - alias to_json rails_to_json end end end diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 5572f61..518ca77 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -120,7 +120,7 @@ module ActiveSupport # # With ActiveSupport.use_standard_json_time_format = false # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json # # => "2005/02/01 15:15:10 +0000" - def rails_to_json(options = nil) + def to_json(options = nil) if ActiveSupport.use_standard_json_time_format xmlschema.inspect else @@ -128,8 +128,6 @@ module ActiveSupport end end - alias to_json rails_to_json - def to_yaml(options = {}) if options.kind_of?(YAML::Emitter) utc.to_yaml(options) diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 823eab0..3211770 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -8,6 +8,12 @@ class TestJSONEncoding < Test::Unit::TestCase end end + class Custom + def to_json(options) + '"custom"' + end + end + TrueTests = [[ true, %(true) ]] FalseTests = [[ false, %(false) ]] NilTests = [[ nil, %(null) ]] @@ -26,6 +32,7 @@ class TestJSONEncoding < Test::Unit::TestCase [ :"a b", %("a b") ]] ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]] + CustomTests = [[ Custom.new, '"custom"' ]] VariableTests = [[ ActiveSupport::JSON::Variable.new('foo'), 'foo'], [ ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")']] @@ -127,12 +134,22 @@ class TestJSONEncoding < Test::Unit::TestCase end class JsonOptionsTests < Test::Unit::TestCase + # The json extension passes internal state to to_json + def test_non_hash_options_should_be_tolerated + faux_internal_state_object = Object.new + + value = Object.new + def value.to_json(options) options end + + assert_kind_of Hash, ActiveSupport::JSON.encode(value, faux_internal_state_object) + end + def test_enumerable_should_passthrough_options_to_elements json_options = { :include => :posts } ActiveSupport::JSON.expects(:encode).with(1, json_options) ActiveSupport::JSON.expects(:encode).with(2, json_options) ActiveSupport::JSON.expects(:encode).with('foo', json_options) - [1, 2, 'foo'].rails_to_json(json_options) + [1, 2, 'foo'].to_json(json_options) end end -- 1.6.4.2 From a69ee11968bfd3113e32feea26f7c39c0ba51d8a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 01:25:32 -0700 Subject: [PATCH 087/779] JSON: split encoding and coercion --- activeresource/lib/active_resource/base.rb | 7 +- activesupport/CHANGELOG | 2 + activesupport/lib/active_support/json.rb | 83 +------------------ activesupport/lib/active_support/json/decoding.rb | 35 ++++++++ .../lib/active_support/json/encoders/date.rb | 17 ++-- .../lib/active_support/json/encoders/date_time.rb | 17 ++-- .../lib/active_support/json/encoders/enumerable.rb | 17 ++-- .../active_support/json/encoders/false_class.rb | 6 +- .../lib/active_support/json/encoders/hash.rb | 30 ++++--- .../lib/active_support/json/encoders/nil_class.rb | 6 +- .../lib/active_support/json/encoders/numeric.rb | 4 + .../lib/active_support/json/encoders/object.rb | 6 +- .../lib/active_support/json/encoders/regexp.rb | 4 + .../lib/active_support/json/encoders/string.rb | 4 + .../lib/active_support/json/encoders/symbol.rb | 4 +- .../lib/active_support/json/encoders/time.rb | 17 ++-- .../lib/active_support/json/encoders/true_class.rb | 6 +- activesupport/lib/active_support/json/encoding.rb | 91 ++++++++++++++++--- activesupport/lib/active_support/time_with_zone.rb | 17 ++-- activesupport/test/json/encoding_test.rb | 2 +- 20 files changed, 212 insertions(+), 163 deletions(-) create mode 100644 activesupport/lib/active_support/json/decoding.rb diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index b04f705..3cce072 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -868,8 +868,7 @@ module ActiveResource attributes.to_xml({:root => self.class.element_name}.merge(options)) end - # Returns a JSON string representing the model. Some configuration is - # available through +options+. + # Coerces to a hash for JSON encoding. # # ==== Options # The +options+ are passed to the +to_json+ method on each @@ -893,8 +892,8 @@ module ActiveResource # # person.to_json(:except => ["first_name"]) # # => {"last_name": "Smith"} - def to_json(options={}) - ActiveSupport::JSON.encode(attributes, options) + def as_json(options = nil) + attributes.as_json(options) end # For compatibility with ActiveSupport::JSON.encode diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index d0e1ce9..9a15bf9 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *2.3.3 (pending)* +* JSON: +Object#to_json+ calls +as_json+ to coerce itself into something natively encodable like +Hash+, +Integer+, or +String+. Override +as_json+ instead of +to_json+ so you're JSON-library-agnostic. [Jeremy Kemper] + * Allow MemCacheStore to be initialized with a MemCache-like object instead of addresses and options [Bryan Helmkamp] diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb index 0b115d7..3e1d9b1 100644 --- a/activesupport/lib/active_support/json.rb +++ b/activesupport/lib/active_support/json.rb @@ -1,83 +1,2 @@ -module ActiveSupport - # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format. - mattr_accessor :use_standard_json_time_format - # Look for and parse json strings that look like ISO 8601 times. - mattr_accessor :parse_json_times - - module JSON - # matches YAML-formatted dates - DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/ - - module Encoding #:nodoc: - mattr_accessor :escape_regex - - ESCAPED_CHARS = { - "\010" => '\b', - "\f" => '\f', - "\n" => '\n', - "\r" => '\r', - "\t" => '\t', - '"' => '\"', - '\\' => '\\\\', - '>' => '\u003E', - '<' => '\u003C', - '&' => '\u0026' - } - - def self.escape(string) - json = '"' + string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] } - json.force_encoding('ascii-8bit') if respond_to?(:force_encoding) - json.gsub(/([\xC0-\xDF][\x80-\xBF]| - [\xE0-\xEF][\x80-\xBF]{2}| - [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| - s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') - } + '"' - end - end - - class << self - delegate :decode, :to => :backend - - def backend - @backend || begin - self.backend = "Yaml" - @backend - end - end - - def backend=(name) - if name.is_a?(Module) - @backend = name - else - require "active_support/json/backends/#{name.to_s.downcase}.rb" - @backend = ActiveSupport::JSON::Backends::const_get(name) - end - end - - def with_backend(name) - old_backend, self.backend = backend, name - yield - ensure - self.backend = old_backend - end - end - end - - class << self - attr_reader :escape_html_entities_in_json - - def escape_html_entities_in_json=(value) - ActiveSupport::JSON::Encoding.escape_regex = \ - if value - /[\010\f\n\r\t"\\><&]/ - else - /[\010\f\n\r\t"\\]/ - end - @escape_html_entities_in_json = value - end - end -end - -ActiveSupport.escape_html_entities_in_json = true - +require 'active_support/json/decoding' require 'active_support/json/encoding' diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb new file mode 100644 index 0000000..5d4caa3 --- /dev/null +++ b/activesupport/lib/active_support/json/decoding.rb @@ -0,0 +1,35 @@ +require 'active_support/core_ext/module/attribute_accessors' + +module ActiveSupport + # Look for and parse json strings that look like ISO 8601 times. + mattr_accessor :parse_json_times + + module JSON + class << self + delegate :decode, :to => :backend + + def backend + @backend || begin + self.backend = "Yaml" + @backend + end + end + + def backend=(name) + if name.is_a?(Module) + @backend = name + else + require "active_support/json/backends/#{name.to_s.downcase}.rb" + @backend = ActiveSupport::JSON::Backends::const_get(name) + end + end + + def with_backend(name) + old_backend, self.backend = backend, name + yield + ensure + self.backend = old_backend + end + end + end +end diff --git a/activesupport/lib/active_support/json/encoders/date.rb b/activesupport/lib/active_support/json/encoders/date.rb index cc84de1..0df1b8e 100644 --- a/activesupport/lib/active_support/json/encoders/date.rb +++ b/activesupport/lib/active_support/json/encoders/date.rb @@ -1,21 +1,22 @@ class Date - # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the - # ISO 8601 format is used. + # Coerces the date to a string for JSON encoding. + # + # ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set. # # ==== Examples # - # # With ActiveSupport.use_standard_json_time_format = true + # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true # Date.new(2005,2,1).to_json # # => "2005-02-01" # - # # With ActiveSupport.use_standard_json_time_format = false + # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false # Date.new(2005,2,1).to_json # # => "2005/02/01" - def to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - %("#{strftime("%Y-%m-%d")}") + def as_json(options = nil) + if ActiveSupport::JSON::Encoding.use_standard_json_time_format + strftime("%Y-%m-%d") else - %("#{strftime("%Y/%m/%d")}") + strftime("%Y/%m/%d") end end end diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb index 6c85824..20c12a7 100644 --- a/activesupport/lib/active_support/json/encoders/date_time.rb +++ b/activesupport/lib/active_support/json/encoders/date_time.rb @@ -1,21 +1,22 @@ class DateTime - # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the - # ISO 8601 format is used. + # Coerces the datetime to a string for JSON encoding. + # + # ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set. # # ==== Examples # - # # With ActiveSupport.use_standard_json_time_format = true + # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true # DateTime.civil(2005,2,1,15,15,10).to_json # # => "2005-02-01T15:15:10+00:00" # - # # With ActiveSupport.use_standard_json_time_format = false + # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false # DateTime.civil(2005,2,1,15,15,10).to_json # # => "2005/02/01 15:15:10 +0000" - def to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - xmlschema.inspect + def as_json(options = nil) + if ActiveSupport::JSON::Encoding.use_standard_json_time_format + xmlschema else - strftime('"%Y/%m/%d %H:%M:%S %z"') + strftime('%Y/%m/%d %H:%M:%S %z') end end end diff --git a/activesupport/lib/active_support/json/encoders/enumerable.rb b/activesupport/lib/active_support/json/encoders/enumerable.rb index 7de7ea4..65924e3 100644 --- a/activesupport/lib/active_support/json/encoders/enumerable.rb +++ b/activesupport/lib/active_support/json/encoders/enumerable.rb @@ -1,18 +1,17 @@ module Enumerable - # Returns a JSON string representing the enumerable. Any +options+ - # given will be passed on to its elements. For example: - # - # users = User.find(:all) - # # => users.to_json(:only => :name) - # - # will pass the :only => :name option to each user. - def to_json(options = nil) #:nodoc: - to_a.to_json(options) + # Coerces the enumerable to an array for JSON encoding. + def as_json(options = nil) #:nodoc: + to_a end end class Array + # Returns a JSON string representing the Array. +options+ are passed to each element. def to_json(options = nil) #:nodoc: "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ','}]" end + + def as_json(options = nil) #:nodoc: + self + end end diff --git a/activesupport/lib/active_support/json/encoders/false_class.rb b/activesupport/lib/active_support/json/encoders/false_class.rb index bf08443..c2bb1ee 100644 --- a/activesupport/lib/active_support/json/encoders/false_class.rb +++ b/activesupport/lib/active_support/json/encoders/false_class.rb @@ -1,5 +1,7 @@ class FalseClass - def to_json(options = nil) #:nodoc: - 'false' + AS_JSON = ActiveSupport::JSON::Variable.new('false').freeze + + def as_json(options = nil) #:nodoc: + AS_JSON end end diff --git a/activesupport/lib/active_support/json/encoders/hash.rb b/activesupport/lib/active_support/json/encoders/hash.rb index 48ee96d..cfe21e4 100644 --- a/activesupport/lib/active_support/json/encoders/hash.rb +++ b/activesupport/lib/active_support/json/encoders/hash.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/wrapper' + class Hash # Returns a JSON string representing the hash. # @@ -29,20 +31,26 @@ class Hash # allowing the posts association in the User model to be converted to JSON # as well. def to_json(options = nil) #:nodoc: - hash_keys = self.keys - - if options - if except = options[:except] - hash_keys = hash_keys - Array.wrap(except) - elsif only = options[:only] - hash_keys = hash_keys & Array.wrap(only) - end - end + hash = as_json(options) result = '{' - result << hash_keys.map do |key| - "#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(self[key], options)}" + result << hash.map do |key, value| + "#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(value, options)}" end * ',' result << '}' end + + def as_json(options = nil) #:nodoc: + if options + if attrs = options[:except] + except(*Array.wrap(attrs)) + elsif attrs = options[:only] + slice(*Array.wrap(attrs)) + else + self + end + else + self + end + end end diff --git a/activesupport/lib/active_support/json/encoders/nil_class.rb b/activesupport/lib/active_support/json/encoders/nil_class.rb index 4763471..041c1a2 100644 --- a/activesupport/lib/active_support/json/encoders/nil_class.rb +++ b/activesupport/lib/active_support/json/encoders/nil_class.rb @@ -1,5 +1,7 @@ class NilClass - def to_json(options = nil) #:nodoc: - 'null' + AS_JSON = ActiveSupport::JSON::Variable.new('null').freeze + + def as_json(options = nil) #:nodoc: + AS_JSON end end diff --git a/activesupport/lib/active_support/json/encoders/numeric.rb b/activesupport/lib/active_support/json/encoders/numeric.rb index 022af18..6493bd6 100644 --- a/activesupport/lib/active_support/json/encoders/numeric.rb +++ b/activesupport/lib/active_support/json/encoders/numeric.rb @@ -2,6 +2,10 @@ class Numeric def to_json(options = nil) #:nodoc: to_s end + + def as_json(options = nil) #:nodoc: + self + end end class Float diff --git a/activesupport/lib/active_support/json/encoders/object.rb b/activesupport/lib/active_support/json/encoders/object.rb index 9c40ebc..0994100 100644 --- a/activesupport/lib/active_support/json/encoders/object.rb +++ b/activesupport/lib/active_support/json/encoders/object.rb @@ -1,6 +1,10 @@ class Object # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. def to_json(options = nil) - ActiveSupport::JSON.encode(instance_values, options) + ActiveSupport::JSON.encode(as_json(options)) + end + + def as_json(options = nil) + instance_values end end diff --git a/activesupport/lib/active_support/json/encoders/regexp.rb b/activesupport/lib/active_support/json/encoders/regexp.rb index b6116b7..14fdbf2 100644 --- a/activesupport/lib/active_support/json/encoders/regexp.rb +++ b/activesupport/lib/active_support/json/encoders/regexp.rb @@ -2,4 +2,8 @@ class Regexp def to_json(options = nil) #:nodoc: inspect end + + def as_json(options = nil) #:nodoc: + self + end end diff --git a/activesupport/lib/active_support/json/encoders/string.rb b/activesupport/lib/active_support/json/encoders/string.rb index 56d869a..697658e 100644 --- a/activesupport/lib/active_support/json/encoders/string.rb +++ b/activesupport/lib/active_support/json/encoders/string.rb @@ -2,4 +2,8 @@ class String def to_json(options = nil) #:nodoc: ActiveSupport::JSON::Encoding.escape(self) end + + def as_json(options = nil) #:nodoc: + self + end end diff --git a/activesupport/lib/active_support/json/encoders/symbol.rb b/activesupport/lib/active_support/json/encoders/symbol.rb index 00d45b6..f7dcd49 100644 --- a/activesupport/lib/active_support/json/encoders/symbol.rb +++ b/activesupport/lib/active_support/json/encoders/symbol.rb @@ -1,5 +1,5 @@ class Symbol - def to_json(options = nil) #:nodoc: - ActiveSupport::JSON.encode(to_s, options) + def as_json(options = nil) #:nodoc: + to_s end end diff --git a/activesupport/lib/active_support/json/encoders/time.rb b/activesupport/lib/active_support/json/encoders/time.rb index f45a005..fa54e9e 100644 --- a/activesupport/lib/active_support/json/encoders/time.rb +++ b/activesupport/lib/active_support/json/encoders/time.rb @@ -1,21 +1,22 @@ class Time - # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the - # ISO 8601 format is used. + # Coerces the time to a string for JSON encoding. + # + # ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set. # # ==== Examples # - # # With ActiveSupport.use_standard_json_time_format = true + # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true # Time.utc(2005,2,1,15,15,10).to_json # # => "2005-02-01T15:15:10Z" # - # # With ActiveSupport.use_standard_json_time_format = false + # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false # Time.utc(2005,2,1,15,15,10).to_json # # => "2005/02/01 15:15:10 +0000" - def to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - xmlschema.inspect + def as_json(options = nil) + if ActiveSupport::JSON::Encoding.use_standard_json_time_format + xmlschema else - %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") + %(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}) end end end diff --git a/activesupport/lib/active_support/json/encoders/true_class.rb b/activesupport/lib/active_support/json/encoders/true_class.rb index 037d812..4b65dee 100644 --- a/activesupport/lib/active_support/json/encoders/true_class.rb +++ b/activesupport/lib/active_support/json/encoders/true_class.rb @@ -1,5 +1,7 @@ class TrueClass - def to_json(options = nil) #:nodoc: - 'true' + AS_JSON = ActiveSupport::JSON::Variable.new('true').freeze + + def as_json(options = nil) #:nodoc: + AS_JSON end end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 3999e1c..fb5c278 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,27 +1,88 @@ -# Hack to load json gem first so we can overwrite its to_json. -begin - require 'json' -rescue LoadError -end +require 'active_support/core_ext/module/delegation' +require 'active_support/deprecation' module ActiveSupport + class << self + delegate :use_standard_json_time_format, :use_standard_json_time_format=, + :escape_html_entities_in_json, :escape_html_entities_in_json=, + :to => :'ActiveSupport::JSON::Encoding' + end + module JSON - class CircularReferenceError < StandardError + # matches YAML-formatted dates + DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/ + + class << self + delegate :encode, :to => :'ActiveSupport::JSON::Encoding' end - # Converts a Ruby object into a JSON string. - def self.encode(value, options = nil) - options = {} unless Hash === options - seen = (options[:seen] ||= []) - raise CircularReferenceError, 'object references itself' if seen.include?(value) - seen << value - value.to_json(options) - ensure - seen.pop + module Encoding #:nodoc: + class CircularReferenceError < StandardError + end + + ESCAPED_CHARS = { + "\010" => '\b', + "\f" => '\f', + "\n" => '\n', + "\r" => '\r', + "\t" => '\t', + '"' => '\"', + '\\' => '\\\\', + '>' => '\u003E', + '<' => '\u003C', + '&' => '\u0026' } + + class << self + # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format. + attr_accessor :use_standard_json_time_format + + attr_accessor :escape_regex + attr_reader :escape_html_entities_in_json + + def escape_html_entities_in_json=(value) + self.escape_regex = \ + if @escape_html_entities_in_json = value + /[\010\f\n\r\t"\\><&]/ + else + /[\010\f\n\r\t"\\]/ + end + end + + def escape(string) + json = '"' + string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] } + json.force_encoding('ascii-8bit') if respond_to?(:force_encoding) + json.gsub(/([\xC0-\xDF][\x80-\xBF]| + [\xE0-\xEF][\x80-\xBF]{2}| + [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| + s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') + } + '"' + end + + # Converts a Ruby object into a JSON string. + def encode(value, options = nil) + options = {} unless Hash === options + seen = (options[:seen] ||= []) + raise CircularReferenceError, 'object references itself' if seen.include?(value) + seen << value + value.to_json(options) + ensure + seen.pop + end + end + + self.escape_html_entities_in_json = true end + + CircularReferenceError = Deprecation::DeprecatedConstantProxy.new('ActiveSupport::JSON::CircularReferenceError', Encoding::CircularReferenceError) end end +# Hack to load json gem first so we can overwrite its to_json. +begin + require 'json' +rescue LoadError +end + require 'active_support/json/variable' require 'active_support/json/encoders/date' require 'active_support/json/encoders/date_time' diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 518ca77..2e5a233 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -108,23 +108,24 @@ module ActiveSupport end alias_method :iso8601, :xmlschema - # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to - # true, the ISO 8601 format is used. + # Coerces the date to a string for JSON encoding. + # + # ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set. # # ==== Examples # - # # With ActiveSupport.use_standard_json_time_format = true + # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json # # => "2005-02-01T15:15:10Z" # - # # With ActiveSupport.use_standard_json_time_format = false + # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json # # => "2005/02/01 15:15:10 +0000" - def to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - xmlschema.inspect + def as_json(options = nil) + if ActiveSupport::JSON::Encoding.use_standard_json_time_format + xmlschema else - %("#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") + %(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}) end end diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 3211770..b42e329 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -82,7 +82,7 @@ class TestJSONEncoding < Test::Unit::TestCase def test_exception_raised_when_encoding_circular_reference a = [1] a << a - assert_raise(ActiveSupport::JSON::CircularReferenceError) { ActiveSupport::JSON.encode(a) } + assert_raise(ActiveSupport::JSON::Encoding::CircularReferenceError) { ActiveSupport::JSON.encode(a) } end def test_hash_key_identifiers_are_always_quoted -- 1.6.4.2 From 01f820c3b25bfb1ec6a25c4b9668ba0fb7bc75d5 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 14:09:16 -0700 Subject: [PATCH 088/779] Use to_json instead of rails_to_json --- .../lib/action_view/helpers/prototype_helper.rb | 2 +- .../active_record/serializers/json_serializer.rb | 3 --- activeresource/lib/active_resource/base.rb | 3 --- 3 files changed, 1 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index e40c5b5..2b9e9db 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -1184,7 +1184,7 @@ module ActionView true end - def rails_to_json(options = nil) + def to_json(options = nil) @variable end diff --git a/activerecord/lib/active_record/serializers/json_serializer.rb b/activerecord/lib/active_record/serializers/json_serializer.rb index 836190e..6fd4730 100644 --- a/activerecord/lib/active_record/serializers/json_serializer.rb +++ b/activerecord/lib/active_record/serializers/json_serializer.rb @@ -80,9 +80,6 @@ module ActiveRecord #:nodoc: end end - # For compatibility with ActiveSupport::JSON.encode - alias rails_to_json to_json - def from_json(json) self.attributes = ActiveSupport::JSON.decode(json) self diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 3cce072..4a4ee9f 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -896,9 +896,6 @@ module ActiveResource attributes.as_json(options) end - # For compatibility with ActiveSupport::JSON.encode - alias rails_to_json to_json - # Returns the serialized string representation of the resource in the configured # serialization format specified in ActiveResource::Base.format. The options # applicable depend on the configured encoding format. -- 1.6.4.2 From ec10f13939fd3b91f863878cc5aae740c67f6def Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 15:16:47 -0700 Subject: [PATCH 089/779] Ruby 1.9: fix json encoding --- activesupport/lib/active_support/json/encoding.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index fb5c278..63d1e71 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,3 +1,4 @@ +# encoding: binary require 'active_support/core_ext/module/delegation' require 'active_support/deprecation' @@ -49,8 +50,8 @@ module ActiveSupport end def escape(string) + string = string.dup.force_encoding(::Encoding::BINARY) if string.respond_to?(:force_encoding) json = '"' + string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] } - json.force_encoding('ascii-8bit') if respond_to?(:force_encoding) json.gsub(/([\xC0-\xDF][\x80-\xBF]| [\xE0-\xEF][\x80-\xBF]{2}| [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| -- 1.6.4.2 From aebd1ba5b49248d88df3bff9ac765f847595ab34 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 20:08:02 -0700 Subject: [PATCH 090/779] Integration tests use Rack::Lint on 1.9 also --- actionpack/lib/action_controller/integration.rb | 7 +------ 1 files changed, 1 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 5f220a4..6f11dac 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -309,12 +309,7 @@ module ActionController ActionController::Base.clear_last_instantiation! - app = @application - # Rack::Lint doesn't accept String headers or bodies in Ruby 1.9 - unless RUBY_VERSION >= '1.9.0' && Rack.release <= '0.9.0' - app = Rack::Lint.new(app) - end - + app = Rack::Lint.new(@application) status, headers, body = app.call(env) @request_count += 1 -- 1.6.4.2 From 05abd7c1963758b6e98277ba360072a6308d5d1a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 20:32:40 -0700 Subject: [PATCH 091/779] Check for to_str instead of String --- actionpack/lib/action_controller/integration.rb | 2 +- actionpack/lib/action_controller/response.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 6f11dac..5178496 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -326,7 +326,7 @@ module ActionController end @body = "" - if body.is_a?(String) + if body.respond_to?(:to_str) @body << body else body.each { |part| @body << part } diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb index 971ef7b..def03fa 100644 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_controller/response.rb @@ -151,7 +151,7 @@ module ActionController # :nodoc: if @body.respond_to?(:call) @writer = lambda { |x| callback.call(x) } @body.call(self, self) - elsif @body.is_a?(String) + elsif @body.respond_to?(:to_str) yield @body else @body.each(&callback) -- 1.6.4.2 From 91fbdfd5b3a0c664e1b80c88e3f60235ac8f7b0e Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 20:33:18 -0700 Subject: [PATCH 092/779] Failsafe doesn't return bare String body --- actionpack/lib/action_controller/failsafe.rb | 2 +- actionpack/test/controller/dispatcher_test.rb | 2 +- actionpack/test/controller/failsafe_test.rb | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_controller/failsafe.rb b/actionpack/lib/action_controller/failsafe.rb index d242585..7f8aee8 100644 --- a/actionpack/lib/action_controller/failsafe.rb +++ b/actionpack/lib/action_controller/failsafe.rb @@ -36,7 +36,7 @@ module ActionController private def failsafe_response(exception) log_failsafe_exception(exception) - [500, {'Content-Type' => 'text/html'}, failsafe_response_body] + [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 diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index 070a43e..df9c961 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -56,7 +56,7 @@ class DispatcherTest < Test::Unit::TestCase assert_equal 3, response.size assert_equal 500, response[0] assert_equal({"Content-Type" => "text/html"}, response[1]) - assert_match /500 Internal Server Error/, response[2] + assert_match /500 Internal Server Error/, response[2].join end def test_prepare_callbacks diff --git a/actionpack/test/controller/failsafe_test.rb b/actionpack/test/controller/failsafe_test.rb index 2593e3a..9008f64 100644 --- a/actionpack/test/controller/failsafe_test.rb +++ b/actionpack/test/controller/failsafe_test.rb @@ -46,7 +46,7 @@ class FailsafeTest < ActionController::TestCase app_will_raise_error! response = @failsafe.call({}) assert_equal 500, response[0] - assert_equal "hello my world", response[2] + assert_equal "hello my world", response[2].join end def test_returns_a_default_message_if_erb_rendering_failed @@ -54,7 +54,7 @@ class FailsafeTest < ActionController::TestCase @failsafe.expects(:render_template).raises(RuntimeError.new("Harddisk is crashing")) response = @failsafe.call({}) assert_equal 500, response[0] - assert_match /500 Internal Server Error/, response[2] - assert_match %r(please read this web application's log file), response[2] + assert_match /500 Internal Server Error/, response[2].join + assert_match %r(please read this web application's log file), response[2].join end -end \ No newline at end of file +end -- 1.6.4.2 From 91727ae5e49b49f299942eb8181fd28312ece889 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 20:07:12 -0700 Subject: [PATCH 093/779] Ruby 1.9: sqlite escape encoding --- .../connection_adapters/sqlite_adapter.rb | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index afd6472..69f7975 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -1,3 +1,4 @@ +# encoding: binary require 'active_record/connection_adapters/abstract_adapter' module ActiveRecord @@ -46,6 +47,7 @@ module ActiveRecord class SQLiteColumn < Column #:nodoc: class << self def string_to_binary(value) + value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding) value.gsub(/\0|\%/n) do |b| case b when "\0" then "%00" @@ -55,6 +57,7 @@ module ActiveRecord end def binary_to_string(value) + value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding) value.gsub(/%00|%25/n) do |b| case b when "%00" then "\0" -- 1.6.4.2 From cc5d313a483fcfa5cceed0bd177a5b54bd5c042c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:49:12 -0700 Subject: [PATCH 094/779] Lazier Rakefile requires to avoid needing full rake gem on 1.9 --- Rakefile | 2 +- actionmailer/Rakefile | 3 ++- actionpack/Rakefile | 3 ++- activerecord/Rakefile | 3 ++- activeresource/Rakefile | 3 ++- railties/Rakefile | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Rakefile b/Rakefile index 352fd63..7ceb4b8 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,5 @@ require 'rake' require 'rake/rdoctask' -require 'rake/contrib/sshpublisher' env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD'] @@ -74,6 +73,7 @@ end desc "Publish API docs for Rails as a whole and for each component" task :pdoc => :rdoc do + require 'rake/contrib/sshpublisher' Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/api", "doc/rdoc").upload PROJECTS.each do |project| system %(cd #{project} && #{env} #{$0} pdoc) diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index f06f18f..83e3f22 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -4,7 +4,6 @@ require 'rake/testtask' require 'rake/rdoctask' require 'rake/packagetask' require 'rake/gempackagetask' -require 'rake/contrib/sshpublisher' require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version') PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' @@ -76,12 +75,14 @@ end desc "Publish the API documentation" task :pgem => [:package] do + require 'rake/contrib/sshpublisher' Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'` end desc "Publish the API documentation" task :pdoc => [:rdoc] do + require 'rake/contrib/sshpublisher' Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/am", "doc").upload end diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 6cacdf3..6217877 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -4,7 +4,6 @@ require 'rake/testtask' require 'rake/rdoctask' require 'rake/packagetask' require 'rake/gempackagetask' -require 'rake/contrib/sshpublisher' require File.join(File.dirname(__FILE__), 'lib', 'action_pack', 'version') PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' @@ -136,12 +135,14 @@ task :update_js => [ :update_scriptaculous ] desc "Publish the API documentation" task :pgem => [:package] do + require 'rake/contrib/sshpublisher' Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'` end desc "Publish the API documentation" task :pdoc => [:rdoc] do + require 'rake/contrib/sshpublisher' Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ap", "doc").upload end diff --git a/activerecord/Rakefile b/activerecord/Rakefile index b50008c..5905c67 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -4,7 +4,6 @@ require 'rake/testtask' require 'rake/rdoctask' require 'rake/packagetask' require 'rake/gempackagetask' -require 'rake/contrib/sshpublisher' require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version') require File.expand_path(File.dirname(__FILE__)) + "/test/config" @@ -231,12 +230,14 @@ end desc "Publish the beta gem" task :pgem => [:package] do + require 'rake/contrib/sshpublisher' Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'` end desc "Publish the API documentation" task :pdoc => [:rdoc] do + require 'rake/contrib/sshpublisher' Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload end diff --git a/activeresource/Rakefile b/activeresource/Rakefile index bf7bbb0..c3cde26 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -4,7 +4,6 @@ require 'rake/testtask' require 'rake/rdoctask' require 'rake/packagetask' require 'rake/gempackagetask' -require 'rake/contrib/sshpublisher' require File.join(File.dirname(__FILE__), 'lib', 'active_resource', 'version') @@ -117,12 +116,14 @@ end desc "Publish the beta gem" task :pgem => [:package] do + require 'rake/contrib/sshpublisher' Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'` end desc "Publish the API documentation" task :pdoc => [:rdoc] do + require 'rake/contrib/sshpublisher' Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload end diff --git a/railties/Rakefile b/railties/Rakefile index 0e8cd9b..792f345 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -2,7 +2,6 @@ require 'rake' require 'rake/testtask' require 'rake/rdoctask' require 'rake/gempackagetask' -require 'rake/contrib/rubyforgepublisher' require 'date' require 'rbconfig' @@ -355,6 +354,7 @@ end desc "Publish the release files to RubyForge." task :release => [ :package ] do + require 'rake/contrib/rubyforgepublisher' require 'rubyforge' packages = %w( gem ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" } -- 1.6.4.2 From f1e75e4378029d61b7b91dfc8a595dc3716bc000 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 17:58:14 -0700 Subject: [PATCH 095/779] Add #element and #collection to ModelName --- .../active_support/core_ext/module/model_naming.rb | 14 ++++++++------ .../test/core_ext/module/model_naming_test.rb | 8 ++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/module/model_naming.rb b/activesupport/lib/active_support/core_ext/module/model_naming.rb index 3ec4f3b..9686be0 100644 --- a/activesupport/lib/active_support/core_ext/module/model_naming.rb +++ b/activesupport/lib/active_support/core_ext/module/model_naming.rb @@ -1,13 +1,15 @@ module ActiveSupport class ModelName < String - attr_reader :singular, :plural, :cache_key, :partial_path + attr_reader :singular, :plural, :element, :collection, :partial_path + alias_method :cache_key, :collection def initialize(name) super - @singular = underscore.tr('/', '_').freeze - @plural = @singular.pluralize.freeze - @cache_key = tableize.freeze - @partial_path = "#{@cache_key}/#{demodulize.underscore}".freeze + @singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze + @plural = ActiveSupport::Inflector.pluralize(@singular).freeze + @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze + @collection = ActiveSupport::Inflector.tableize(self).freeze + @partial_path = "#{@collection}/#{@element}".freeze end end @@ -16,7 +18,7 @@ module ActiveSupport # Returns an ActiveSupport::ModelName object for module. It can be # used to retrieve all kinds of naming-related information. def model_name - @model_name ||= ModelName.new(name) + @model_name ||= ::ActiveSupport::ModelName.new(name) end end end diff --git a/activesupport/test/core_ext/module/model_naming_test.rb b/activesupport/test/core_ext/module/model_naming_test.rb index d08349d..b0e46c4 100644 --- a/activesupport/test/core_ext/module/model_naming_test.rb +++ b/activesupport/test/core_ext/module/model_naming_test.rb @@ -13,6 +13,14 @@ class ModelNamingTest < Test::Unit::TestCase assert_equal 'post_track_backs', @model_name.plural end + def test_element + assert_equal 'track_back', @model_name.element + end + + def test_collection + assert_equal 'post/track_backs', @model_name.collection + end + def test_partial_path assert_equal 'post/track_backs/track_back', @model_name.partial_path end -- 1.6.4.2 From 74f16a56e71177f2ca60f33b0ece1c2c551fc39f Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:29:24 -0700 Subject: [PATCH 096/779] Simplify json decoder backend lazy load --- activesupport/lib/active_support/json/decoding.rb | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index 5d4caa3..b4e4177 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -9,10 +9,8 @@ module ActiveSupport delegate :decode, :to => :backend def backend - @backend || begin - self.backend = "Yaml" - @backend - end + self.backend = "Yaml" unless defined?(@backend) + @backend end def backend=(name) -- 1.6.4.2 From 756e82d1b6fbd42d7223786cdebb981465092783 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:30:27 -0700 Subject: [PATCH 097/779] Prefer JSON.encode(value) to value.to_json --- .../lib/active_resource/formats/json_format.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activeresource/lib/active_resource/formats/json_format.rb b/activeresource/lib/active_resource/formats/json_format.rb index 1d88fc5..127e828 100644 --- a/activeresource/lib/active_resource/formats/json_format.rb +++ b/activeresource/lib/active_resource/formats/json_format.rb @@ -11,8 +11,8 @@ module ActiveResource "application/json" end - def encode(hash, options={}) - hash.to_json(options) + def encode(hash, options = nil) + ActiveSupport::JSON.encode(hash, options) end def decode(json) -- 1.6.4.2 From f9b22276493a127bb9099295bd6c4d8caa10de04 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:49:49 -0700 Subject: [PATCH 098/779] Qualify constant references in BasicObjects --- .../lib/action_view/helpers/prototype_helper.rb | 14 +++++++------- 1 files changed, 7 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 2b9e9db..448d6f5 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -686,7 +686,7 @@ module ActionView # Returns an object whose to_json evaluates to +code+. Use this to pass a literal JavaScript # expression as an argument to another JavaScriptGenerator method. def literal(code) - ActiveSupport::JSON::Variable.new(code.to_s) + ::ActiveSupport::JSON::Variable.new(code.to_s) end # Returns a collection reference by finding it through a CSS +pattern+ in the DOM. This collection can then be @@ -973,7 +973,7 @@ module ActionView def loop_on_multiple_args(method, ids) record(ids.size>1 ? "#{javascript_object_for(ids)}.each(#{method})" : - "#{method}(#{ActiveSupport::JSON.encode(ids.first)})") + "#{method}(#{::ActiveSupport::JSON.encode(ids.first)})") end def page @@ -997,7 +997,7 @@ module ActionView end def javascript_object_for(object) - ActiveSupport::JSON.encode(object) + ::ActiveSupport::JSON.encode(object) end def arguments_for_call(arguments, block = nil) @@ -1139,7 +1139,7 @@ module ActionView class JavaScriptElementProxy < JavaScriptProxy #:nodoc: def initialize(generator, id) @id = id - super(generator, "$(#{ActiveSupport::JSON.encode(id)})") + super(generator, "$(#{::ActiveSupport::JSON.encode(id)})") end # Allows access of element attributes through +attribute+. Examples: @@ -1211,7 +1211,7 @@ module ActionView enumerate :eachSlice, :variable => variable, :method_args => [number], :yield_args => %w(value index), :return => true, &block else add_variable_assignment!(variable) - append_enumerable_function!("eachSlice(#{ActiveSupport::JSON.encode(number)});") + append_enumerable_function!("eachSlice(#{::ActiveSupport::JSON.encode(number)});") end end @@ -1232,7 +1232,7 @@ module ActionView def pluck(variable, property) add_variable_assignment!(variable) - append_enumerable_function!("pluck(#{ActiveSupport::JSON.encode(property)});") + append_enumerable_function!("pluck(#{::ActiveSupport::JSON.encode(property)});") end def zip(variable, *arguments, &block) @@ -1296,7 +1296,7 @@ module ActionView class JavaScriptElementCollectionProxy < JavaScriptCollectionProxy #:nodoc:\ def initialize(generator, pattern) - super(generator, "$$(#{ActiveSupport::JSON.encode(pattern)})") + super(generator, "$$(#{::ActiveSupport::JSON.encode(pattern)})") end end end -- 1.6.4.2 From 63d0c337875eee99dd6fe8fc0dd6949c0a06c21c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:25:56 -0700 Subject: [PATCH 099/779] Fix AR json encoding --- activerecord/lib/active_record/serialization.rb | 5 ++- .../active_record/serializers/json_serializer.rb | 27 ++++++-------------- activerecord/test/cases/json_serialization_test.rb | 8 +++--- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb index 870b4b2..629ce9a 100644 --- a/activerecord/lib/active_record/serialization.rb +++ b/activerecord/lib/active_record/serialization.rb @@ -5,8 +5,9 @@ module ActiveRecord #:nodoc: class Serializer #:nodoc: attr_reader :options - def initialize(record, options = {}) - @record, @options = record, options.dup + def initialize(record, options = nil) + @record = record + @options = options ? options.dup : {} end # To replicate the behavior in ActiveRecord#attributes, diff --git a/activerecord/lib/active_record/serializers/json_serializer.rb b/activerecord/lib/active_record/serializers/json_serializer.rb index 6fd4730..085d4f1 100644 --- a/activerecord/lib/active_record/serializers/json_serializer.rb +++ b/activerecord/lib/active_record/serializers/json_serializer.rb @@ -1,8 +1,10 @@ +require 'active_support/json' +require 'active_support/core_ext/module/model_naming' + module ActiveRecord #:nodoc: module Serialization def self.included(base) base.cattr_accessor :include_root_in_json, :instance_writer => false - base.extend ClassMethods end # Returns a JSON string representing the model. Some configuration is @@ -72,29 +74,16 @@ module ActiveRecord #:nodoc: # {"comments": [{"body": "Don't think too hard"}], # "title": "So I was thinking"}]} def to_json(options = {}) - json = JsonSerializer.new(self, options).to_s - if include_root_in_json - "{#{self.class.json_class_name}:#{json}}" - else - json - end + hash = Serializer.new(self, options).serializable_record + hash = { self.class.model_name.element => hash } if include_root_in_json + ActiveSupport::JSON.encode(hash) end + def as_json(options = nil) self end #:nodoc: + def from_json(json) self.attributes = ActiveSupport::JSON.decode(json) self end - - class JsonSerializer < ActiveRecord::Serialization::Serializer #:nodoc: - def serialize - ActiveSupport::JSON.encode(serializable_record) - end - end - - module ClassMethods - def json_class_name - @json_class_name ||= name.demodulize.underscore.inspect - end - end end end diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb index 0ffbc9b..54bc8e2 100644 --- a/activerecord/test/cases/json_serialization_test.rb +++ b/activerecord/test/cases/json_serialization_test.rb @@ -170,18 +170,18 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase def test_should_allow_only_option_for_list_of_authors authors = [@david, @mary] - assert_equal %([{"name":"David"},{"name":"Mary"}]), authors.to_json(:only => :name) + assert_equal %([{"name":"David"},{"name":"Mary"}]), ActiveSupport::JSON.encode(authors, :only => :name) end def test_should_allow_except_option_for_list_of_authors authors = [@david, @mary] - assert_equal %([{"id":1},{"id":2}]), authors.to_json(:except => [:name, :author_address_id, :author_address_extra_id]) + assert_equal %([{"id":1},{"id":2}]), ActiveSupport::JSON.encode(authors, :except => [:name, :author_address_id, :author_address_extra_id]) end def test_should_allow_includes_for_list_of_authors authors = [@david, @mary] - json = authors.to_json( + json = ActiveSupport::JSON.encode(authors, :only => :name, :include => { :posts => { :only => :id } @@ -200,6 +200,6 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase 2 => @mary } - assert_equal %({"1":{"name":"David"}}), authors_hash.to_json(:only => [1, :name]) + assert_equal %({"1":{"name":"David"}}), ActiveSupport::JSON.encode(authors_hash, :only => [1, :name]) end end -- 1.6.4.2 From e70272e2a411ce5c548e15ef107bb63749f99026 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 20 Apr 2009 00:40:15 -0700 Subject: [PATCH 100/779] Clearer String#first and #last edge cases. Fix that 'foo'.first(0) == 'foo' instead of '' --- .../lib/active_support/core_ext/string/access.rb | 16 ++++++++++++++-- 1 files changed, 14 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/access.rb b/activesupport/lib/active_support/core_ext/string/access.rb index e067f93..e806b32 100644 --- a/activesupport/lib/active_support/core_ext/string/access.rb +++ b/activesupport/lib/active_support/core_ext/string/access.rb @@ -81,11 +81,23 @@ module ActiveSupport #:nodoc: end def first(limit = 1) - self[0..(limit - 1)] + if limit == 0 + '' + elsif limit >= size + self + else + to(limit - 1) + end end def last(limit = 1) - from(-limit) || self + if limit == 0 + '' + elsif limit >= size + self + else + from(-limit) + end end end end -- 1.6.4.2 From 5fb66a3abb4789b00158928efb5a83c0ad0004b6 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 1 May 2009 20:12:49 +0100 Subject: [PATCH 101/779] Vendor Rack edge ( commit : 815342a8e15db564b766f209ffb1e340233f064f ) --- actionpack/lib/action_controller.rb | 8 +- .../action_controller/vendor/rack-1.1.pre/rack.rb | 90 ++++ .../vendor/rack-1.1.pre/rack/adapter/camping.rb | 22 + .../rack-1.1.pre/rack/auth/abstract/handler.rb | 37 ++ .../rack-1.1.pre/rack/auth/abstract/request.rb | 37 ++ .../vendor/rack-1.1.pre/rack/auth/basic.rb | 58 +++ .../vendor/rack-1.1.pre/rack/auth/digest/md5.rb | 124 +++++ .../vendor/rack-1.1.pre/rack/auth/digest/nonce.rb | 51 ++ .../vendor/rack-1.1.pre/rack/auth/digest/params.rb | 55 ++ .../rack-1.1.pre/rack/auth/digest/request.rb | 40 ++ .../vendor/rack-1.1.pre/rack/auth/openid.rb | 480 +++++++++++++++++ .../vendor/rack-1.1.pre/rack/builder.rb | 63 +++ .../vendor/rack-1.1.pre/rack/cascade.rb | 36 ++ .../vendor/rack-1.1.pre/rack/chunked.rb | 49 ++ .../vendor/rack-1.1.pre/rack/commonlogger.rb | 61 +++ .../vendor/rack-1.1.pre/rack/conditionalget.rb | 47 ++ .../vendor/rack-1.1.pre/rack/content_length.rb | 29 + .../vendor/rack-1.1.pre/rack/content_type.rb | 23 + .../vendor/rack-1.1.pre/rack/deflater.rb | 96 ++++ .../vendor/rack-1.1.pre/rack/directory.rb | 153 ++++++ .../vendor/rack-1.1.pre/rack/file.rb | 88 ++++ .../vendor/rack-1.1.pre/rack/handler.rb | 69 +++ .../vendor/rack-1.1.pre/rack/handler/cgi.rb | 61 +++ .../rack-1.1.pre/rack/handler/evented_mongrel.rb | 8 + .../vendor/rack-1.1.pre/rack/handler/fastcgi.rb | 88 ++++ .../vendor/rack-1.1.pre/rack/handler/lsws.rb | 55 ++ .../vendor/rack-1.1.pre/rack/handler/mongrel.rb | 84 +++ .../vendor/rack-1.1.pre/rack/handler/scgi.rb | 59 +++ .../rack/handler/swiftiplied_mongrel.rb | 8 + .../vendor/rack-1.1.pre/rack/handler/thin.rb | 18 + .../vendor/rack-1.1.pre/rack/handler/webrick.rb | 67 +++ .../vendor/rack-1.1.pre/rack/head.rb | 19 + .../vendor/rack-1.1.pre/rack/lint.rb | 537 ++++++++++++++++++++ .../vendor/rack-1.1.pre/rack/lobster.rb | 65 +++ .../vendor/rack-1.1.pre/rack/lock.rb | 16 + .../vendor/rack-1.1.pre/rack/methodoverride.rb | 27 + .../vendor/rack-1.1.pre/rack/mime.rb | 204 ++++++++ .../vendor/rack-1.1.pre/rack/mock.rb | 184 +++++++ .../vendor/rack-1.1.pre/rack/recursive.rb | 57 ++ .../vendor/rack-1.1.pre/rack/reloader.rb | 106 ++++ .../vendor/rack-1.1.pre/rack/request.rb | 254 +++++++++ .../vendor/rack-1.1.pre/rack/response.rb | 183 +++++++ .../vendor/rack-1.1.pre/rack/rewindable_input.rb | 98 ++++ .../rack-1.1.pre/rack/session/abstract/id.rb | 142 +++++ .../vendor/rack-1.1.pre/rack/session/cookie.rb | 91 ++++ .../vendor/rack-1.1.pre/rack/session/memcache.rb | 109 ++++ .../vendor/rack-1.1.pre/rack/session/pool.rb | 100 ++++ .../vendor/rack-1.1.pre/rack/showexceptions.rb | 349 +++++++++++++ .../vendor/rack-1.1.pre/rack/showstatus.rb | 106 ++++ .../vendor/rack-1.1.pre/rack/static.rb | 38 ++ .../vendor/rack-1.1.pre/rack/urlmap.rb | 55 ++ .../vendor/rack-1.1.pre/rack/utils.rb | 516 +++++++++++++++++++ 52 files changed, 5418 insertions(+), 2 deletions(-) create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/adapter/camping.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/request.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/basic.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/md5.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/params.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/request.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/openid.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/builder.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/cascade.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/chunked.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/commonlogger.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/conditionalget.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_length.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_type.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/deflater.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/directory.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/file.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/cgi.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/fastcgi.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/lsws.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/mongrel.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/scgi.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/thin.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/webrick.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/head.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lint.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lobster.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lock.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/methodoverride.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mime.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mock.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/recursive.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/reloader.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/request.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/response.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/rewindable_input.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/abstract/id.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/cookie.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/memcache.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/pool.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showexceptions.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showstatus.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/static.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/urlmap.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/utils.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 8f8820e..28702cb 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -31,8 +31,12 @@ rescue LoadError end end -gem 'rack', '~> 1.0.0' -require 'rack' +begin + gem 'rack', '~> 1.1.0' + require 'rack' +rescue Gem::LoadError + require 'action_controller/vendor/rack-1.1.pre/rack' +end module ActionController # TODO: Review explicit to see if they will automatically be handled by diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack.rb new file mode 100644 index 0000000..371d015 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack.rb @@ -0,0 +1,90 @@ +# Copyright (C) 2007, 2008, 2009 Christian Neukirchen +# +# Rack is freely distributable under the terms of an MIT-style license. +# See COPYING or http://www.opensource.org/licenses/mit-license.php. + +path = File.expand_path(File.dirname(__FILE__)) +$:.unshift(path) unless $:.include?(path) + + +# The Rack main module, serving as a namespace for all core Rack +# modules and classes. +# +# All modules meant for use in your application are autoloaded here, +# so it should be enough just to require rack.rb in your code. + +module Rack + # The Rack protocol version number implemented. + VERSION = [1,0] + + # Return the Rack protocol version as a dotted string. + def self.version + VERSION.join(".") + end + + # Return the Rack release as a dotted string. + def self.release + "1.0" + end + + autoload :Builder, "rack/builder" + autoload :Cascade, "rack/cascade" + autoload :Chunked, "rack/chunked" + autoload :CommonLogger, "rack/commonlogger" + autoload :ConditionalGet, "rack/conditionalget" + autoload :ContentLength, "rack/content_length" + autoload :ContentType, "rack/content_type" + autoload :File, "rack/file" + autoload :Deflater, "rack/deflater" + autoload :Directory, "rack/directory" + autoload :ForwardRequest, "rack/recursive" + autoload :Handler, "rack/handler" + autoload :Head, "rack/head" + autoload :Lint, "rack/lint" + autoload :Lock, "rack/lock" + autoload :MethodOverride, "rack/methodoverride" + autoload :Mime, "rack/mime" + autoload :Recursive, "rack/recursive" + autoload :Reloader, "rack/reloader" + autoload :ShowExceptions, "rack/showexceptions" + autoload :ShowStatus, "rack/showstatus" + autoload :Static, "rack/static" + autoload :URLMap, "rack/urlmap" + autoload :Utils, "rack/utils" + + autoload :MockRequest, "rack/mock" + autoload :MockResponse, "rack/mock" + + autoload :Request, "rack/request" + autoload :Response, "rack/response" + + module Auth + autoload :Basic, "rack/auth/basic" + autoload :AbstractRequest, "rack/auth/abstract/request" + autoload :AbstractHandler, "rack/auth/abstract/handler" + autoload :OpenID, "rack/auth/openid" + module Digest + autoload :MD5, "rack/auth/digest/md5" + autoload :Nonce, "rack/auth/digest/nonce" + autoload :Params, "rack/auth/digest/params" + autoload :Request, "rack/auth/digest/request" + end + end + + module Session + autoload :Cookie, "rack/session/cookie" + autoload :Pool, "rack/session/pool" + autoload :Memcache, "rack/session/memcache" + end + + # *Adapters* connect Rack with third party web frameworks. + # + # Rack includes an adapter for Camping, see README for other + # frameworks supporting Rack in their code bases. + # + # Refer to the submodules for framework-specific calling details. + + module Adapter + autoload :Camping, "rack/adapter/camping" + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/adapter/camping.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/adapter/camping.rb new file mode 100644 index 0000000..63bc787 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/adapter/camping.rb @@ -0,0 +1,22 @@ +module Rack + module Adapter + class Camping + def initialize(app) + @app = app + end + + def call(env) + env["PATH_INFO"] ||= "" + env["SCRIPT_NAME"] ||= "" + controller = @app.run(env['rack.input'], env) + h = controller.headers + h.each_pair do |k,v| + if v.kind_of? URI + h[k] = v.to_s + end + end + [controller.status, controller.headers, [controller.body.to_s]] + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb new file mode 100644 index 0000000..214df62 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb @@ -0,0 +1,37 @@ +module Rack + module Auth + # Rack::Auth::AbstractHandler implements common authentication functionality. + # + # +realm+ should be set for all handlers. + + class AbstractHandler + + attr_accessor :realm + + def initialize(app, realm=nil, &authenticator) + @app, @realm, @authenticator = app, realm, authenticator + end + + + private + + def unauthorized(www_authenticate = challenge) + return [ 401, + { 'Content-Type' => 'text/plain', + 'Content-Length' => '0', + 'WWW-Authenticate' => www_authenticate.to_s }, + [] + ] + end + + def bad_request + return [ 400, + { 'Content-Type' => 'text/plain', + 'Content-Length' => '0' }, + [] + ] + end + + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/request.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/request.rb new file mode 100644 index 0000000..1d9ccec --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/request.rb @@ -0,0 +1,37 @@ +module Rack + module Auth + class AbstractRequest + + def initialize(env) + @env = env + end + + def provided? + !authorization_key.nil? + end + + def parts + @parts ||= @env[authorization_key].split(' ', 2) + end + + def scheme + @scheme ||= parts.first.downcase.to_sym + end + + def params + @params ||= parts.last + end + + + private + + AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION'] + + def authorization_key + @authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) } + end + + end + + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/basic.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/basic.rb new file mode 100644 index 0000000..9557224 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/basic.rb @@ -0,0 +1,58 @@ +require 'rack/auth/abstract/handler' +require 'rack/auth/abstract/request' + +module Rack + module Auth + # Rack::Auth::Basic implements HTTP Basic Authentication, as per RFC 2617. + # + # Initialize with the Rack application that you want protecting, + # and a block that checks if a username and password pair are valid. + # + # See also: example/protectedlobster.rb + + class Basic < AbstractHandler + + def call(env) + auth = Basic::Request.new(env) + + return unauthorized unless auth.provided? + + return bad_request unless auth.basic? + + if valid?(auth) + env['REMOTE_USER'] = auth.username + + return @app.call(env) + end + + unauthorized + end + + + private + + def challenge + 'Basic realm="%s"' % realm + end + + def valid?(auth) + @authenticator.call(*auth.credentials) + end + + class Request < Auth::AbstractRequest + def basic? + :basic == scheme + end + + def credentials + @credentials ||= params.unpack("m*").first.split(/:/, 2) + end + + def username + credentials.first + end + end + + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/md5.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/md5.rb new file mode 100644 index 0000000..e579dc9 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/md5.rb @@ -0,0 +1,124 @@ +require 'rack/auth/abstract/handler' +require 'rack/auth/digest/request' +require 'rack/auth/digest/params' +require 'rack/auth/digest/nonce' +require 'digest/md5' + +module Rack + module Auth + module Digest + # Rack::Auth::Digest::MD5 implements the MD5 algorithm version of + # HTTP Digest Authentication, as per RFC 2617. + # + # Initialize with the [Rack] application that you want protecting, + # and a block that looks up a plaintext password for a given username. + # + # +opaque+ needs to be set to a constant base64/hexadecimal string. + # + class MD5 < AbstractHandler + + attr_accessor :opaque + + attr_writer :passwords_hashed + + def initialize(*args) + super + @passwords_hashed = nil + end + + def passwords_hashed? + !!@passwords_hashed + end + + def call(env) + auth = Request.new(env) + + unless auth.provided? + return unauthorized + end + + if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth) + return bad_request + end + + if valid?(auth) + if auth.nonce.stale? + return unauthorized(challenge(:stale => true)) + else + env['REMOTE_USER'] = auth.username + + return @app.call(env) + end + end + + unauthorized + end + + + private + + QOP = 'auth'.freeze + + def params(hash = {}) + Params.new do |params| + params['realm'] = realm + params['nonce'] = Nonce.new.to_s + params['opaque'] = H(opaque) + params['qop'] = QOP + + hash.each { |k, v| params[k] = v } + end + end + + def challenge(hash = {}) + "Digest #{params(hash)}" + end + + def valid?(auth) + valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth) + end + + def valid_qop?(auth) + QOP == auth.qop + end + + def valid_opaque?(auth) + H(opaque) == auth.opaque + end + + def valid_nonce?(auth) + auth.nonce.valid? + end + + def valid_digest?(auth) + digest(auth, @authenticator.call(auth.username)) == auth.response + end + + def md5(data) + ::Digest::MD5.hexdigest(data) + end + + alias :H :md5 + + def KD(secret, data) + H([secret, data] * ':') + end + + def A1(auth, password) + [ auth.username, auth.realm, password ] * ':' + end + + def A2(auth) + [ auth.method, auth.uri ] * ':' + end + + def digest(auth, password) + password_hash = passwords_hashed? ? password : H(A1(auth, password)) + + KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, QOP, H(A2(auth)) ] * ':') + end + + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb new file mode 100644 index 0000000..dbe109f --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb @@ -0,0 +1,51 @@ +require 'digest/md5' + +module Rack + module Auth + module Digest + # Rack::Auth::Digest::Nonce is the default nonce generator for the + # Rack::Auth::Digest::MD5 authentication handler. + # + # +private_key+ needs to set to a constant string. + # + # +time_limit+ can be optionally set to an integer (number of seconds), + # to limit the validity of the generated nonces. + + class Nonce + + class << self + attr_accessor :private_key, :time_limit + end + + def self.parse(string) + new(*string.unpack("m*").first.split(' ', 2)) + end + + def initialize(timestamp = Time.now, given_digest = nil) + @timestamp, @given_digest = timestamp.to_i, given_digest + end + + def to_s + [([ @timestamp, digest ] * ' ')].pack("m*").strip + end + + def digest + ::Digest::MD5.hexdigest([ @timestamp, self.class.private_key ] * ':') + end + + def valid? + digest == @given_digest + end + + def stale? + !self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit + end + + def fresh? + !stale? + end + + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/params.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/params.rb new file mode 100644 index 0000000..730e2ef --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/params.rb @@ -0,0 +1,55 @@ +module Rack + module Auth + module Digest + class Params < Hash + + def self.parse(str) + split_header_value(str).inject(new) do |header, param| + k, v = param.split('=', 2) + header[k] = dequote(v) + header + end + end + + def self.dequote(str) # From WEBrick::HTTPUtils + ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup + ret.gsub!(/\\(.)/, "\\1") + ret + end + + def self.split_header_value(str) + str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] } + end + + def initialize + super + + yield self if block_given? + end + + def [](k) + super k.to_s + end + + def []=(k, v) + super k.to_s, v.to_s + end + + UNQUOTED = ['qop', 'nc', 'stale'] + + def to_s + inject([]) do |parts, (k, v)| + parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v)) + parts + end.join(', ') + end + + def quote(str) # From WEBrick::HTTPUtils + '"' << str.gsub(/[\\\"]/o, "\\\1") << '"' + end + + end + end + end +end + diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/request.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/request.rb new file mode 100644 index 0000000..a8aa3bf --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/request.rb @@ -0,0 +1,40 @@ +require 'rack/auth/abstract/request' +require 'rack/auth/digest/params' +require 'rack/auth/digest/nonce' + +module Rack + module Auth + module Digest + class Request < Auth::AbstractRequest + + def method + @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD'] + end + + def digest? + :digest == scheme + end + + def correct_uri? + (@env['SCRIPT_NAME'].to_s + @env['PATH_INFO'].to_s) == uri + end + + def nonce + @nonce ||= Nonce.parse(params['nonce']) + end + + def params + @params ||= Params.parse(parts.last) + end + + def method_missing(sym) + if params.has_key? key = sym.to_s + return params[key] + end + super + end + + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/openid.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/openid.rb new file mode 100644 index 0000000..c5f6a51 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/openid.rb @@ -0,0 +1,480 @@ +# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net + +gem 'ruby-openid', '~> 2' if defined? Gem +require 'rack/request' +require 'rack/utils' +require 'rack/auth/abstract/handler' +require 'uri' +require 'openid' #gem +require 'openid/extension' #gem +require 'openid/store/memory' #gem + +module Rack + class Request + def openid_request + @env['rack.auth.openid.request'] + end + + def openid_response + @env['rack.auth.openid.response'] + end + end + + module Auth + + # Rack::Auth::OpenID provides a simple method for setting up an OpenID + # Consumer. It requires the ruby-openid library from janrain to operate, + # as well as a rack method of session management. + # + # The ruby-openid home page is at http://openidenabled.com/ruby-openid/. + # + # The OpenID specifications can be found at + # http://openid.net/specs/openid-authentication-1_1.html + # and + # http://openid.net/specs/openid-authentication-2_0.html. Documentation + # for published OpenID extensions and related topics can be found at + # http://openid.net/developers/specs/. + # + # It is recommended to read through the OpenID spec, as well as + # ruby-openid's documentation, to understand what exactly goes on. However + # a setup as simple as the presented examples is enough to provide + # Consumer functionality. + # + # This library strongly intends to utilize the OpenID 2.0 features of the + # ruby-openid library, which provides OpenID 1.0 compatiblity. + # + # NOTE: Due to the amount of data that this library stores in the + # session, Rack::Session::Cookie may fault. + + class OpenID + + class NoSession < RuntimeError; end + class BadExtension < RuntimeError; end + # Required for ruby-openid + ValidStatus = [:success, :setup_needed, :cancel, :failure] + + # = Arguments + # + # The first argument is the realm, identifying the site they are trusting + # with their identity. This is required, also treated as the trust_root + # in OpenID 1.x exchanges. + # + # The optional second argument is a hash of options. + # + # == Options + # + # :return_to defines the url to return to after the client + # authenticates with the openid service provider. This url should point + # to where Rack::Auth::OpenID is mounted. If :return_to is not + # provided, return_to will be the current url which allows flexibility + # with caveats. + # + # :session_key defines the key to the session hash in the env. + # It defaults to 'rack.session'. + # + # :openid_param defines at what key in the request parameters to + # find the identifier to resolve. As per the 2.0 spec, the default is + # 'openid_identifier'. + # + # :store defined what OpenID Store to use for persistant + # information. By default a Store::Memory will be used. + # + # :immediate as true will make initial requests to be of an + # immediate type. This is false by default. See OpenID specification + # documentation. + # + # :extensions should be a hash of openid extension + # implementations. The key should be the extension main module, the value + # should be an array of arguments for extension::Request.new. + # The hash is iterated over and passed to #add_extension for processing. + # Please see #add_extension for further documentation. + # + # == Examples + # + # simple_oid = OpenID.new('http://mysite.com/') + # + # return_oid = OpenID.new('http://mysite.com/', { + # :return_to => 'http://mysite.com/openid' + # }) + # + # complex_oid = OpenID.new('http://mysite.com/', + # :immediate => true, + # :extensions => { + # ::OpenID::SReg => [['email'],['nickname']] + # } + # ) + # + # = Advanced + # + # Most of the functionality of this library is encapsulated such that + # expansion and overriding functions isn't difficult nor tricky. + # Alternately, to avoid opening up singleton objects or subclassing, a + # wrapper rack middleware can be composed to act upon Auth::OpenID's + # responses. See #check and #finish for locations of pertinent data. + # + # == Responses + # + # To change the responses that Auth::OpenID returns, override the methods + # #redirect, #bad_request, #unauthorized, #access_denied, and + # #foreign_server_failure. + # + # Additionally #confirm_post_params is used when the URI would exceed + # length limits on a GET request when doing the initial verification + # request. + # + # == Processing + # + # To change methods of processing completed transactions, override the + # methods #success, #setup_needed, #cancel, and #failure. Please ensure + # the returned object is a rack compatible response. + # + # The first argument is an OpenID::Response, the second is a + # Rack::Request of the current request, the last is the hash used in + # ruby-openid handling, which can be found manually at + # env['rack.session'][:openid]. + # + # This is useful if you wanted to expand the processing done, such as + # setting up user accounts. + # + # oid_app = Rack::Auth::OpenID.new realm, :return_to => return_to + # def oid_app.success oid, request, session + # user = Models::User[oid.identity_url] + # user ||= Models::User.create_from_openid oid + # request['rack.session'][:user] = user.id + # redirect MyApp.site_home + # end + # + # site_map['/openid'] = oid_app + # map = Rack::URLMap.new site_map + # ... + + def initialize(realm, options={}) + realm = URI(realm) + raise ArgumentError, "Invalid realm: #{realm}" \ + unless realm.absolute? \ + and realm.fragment.nil? \ + and realm.scheme =~ /^https?$/ \ + and realm.host =~ /^(\*\.)?#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+/ + realm.path = '/' if realm.path.empty? + @realm = realm.to_s + + if ruri = options[:return_to] + ruri = URI(ruri) + raise ArgumentError, "Invalid return_to: #{ruri}" \ + unless ruri.absolute? \ + and ruri.scheme =~ /^https?$/ \ + and ruri.fragment.nil? + raise ArgumentError, "return_to #{ruri} not within realm #{realm}" \ + unless self.within_realm?(ruri) + @return_to = ruri.to_s + end + + @session_key = options[:session_key] || 'rack.session' + @openid_param = options[:openid_param] || 'openid_identifier' + @store = options[:store] || ::OpenID::Store::Memory.new + @immediate = !!options[:immediate] + + @extensions = {} + if extensions = options.delete(:extensions) + extensions.each do |ext, args| + add_extension ext, *args + end + end + + # Undocumented, semi-experimental + @anonymous = !!options[:anonymous] + end + + attr_reader :realm, :return_to, :session_key, :openid_param, :store, + :immediate, :extensions + + # Sets up and uses session data at :openid within the session. + # Errors in this setup will raise a NoSession exception. + # + # If the parameter 'openid.mode' is set, which implies a followup from + # the openid server, processing is passed to #finish and the result is + # returned. However, if there is no appropriate openid information in the + # session, a 400 error is returned. + # + # If the parameter specified by options[:openid_param] is + # present, processing is passed to #check and the result is returned. + # + # If neither of these conditions are met, #unauthorized is called. + + def call(env) + env['rack.auth.openid'] = self + env_session = env[@session_key] + unless env_session and env_session.is_a?(Hash) + raise NoSession, 'No compatible session' + end + # let us work in our own namespace... + session = (env_session[:openid] ||= {}) + unless session and session.is_a?(Hash) + raise NoSession, 'Incompatible openid session' + end + + request = Rack::Request.new(env) + consumer = ::OpenID::Consumer.new(session, @store) + + if mode = request.GET['openid.mode'] + if session.key?(:openid_param) + finish(consumer, session, request) + else + bad_request + end + elsif request.GET[@openid_param] + check(consumer, session, request) + else + unauthorized + end + end + + # As the first part of OpenID consumer action, #check retrieves the data + # required for completion. + # + # If all parameters fit within the max length of a URI, a 303 redirect + # will be returned. Otherwise #confirm_post_params will be called. + # + # Any messages from OpenID's request are logged to env['rack.errors'] + # + # env['rack.auth.openid.request'] is the openid checkid request + # instance. + # + # session[:openid_param] is set to the openid identifier + # provided by the user. + # + # session[:return_to] is set to the return_to uri given to the + # identity provider. + + def check(consumer, session, req) + oid = consumer.begin(req.GET[@openid_param], @anonymous) + req.env['rack.auth.openid.request'] = oid + req.env['rack.errors'].puts(oid.message) + p oid if $DEBUG + + ## Extension support + extensions.each do |ext,args| + oid.add_extension(ext::Request.new(*args)) + end + + session[:openid_param] = req.GET[openid_param] + return_to_uri = return_to ? return_to : req.url + session[:return_to] = return_to_uri + immediate = session.key?(:setup_needed) ? false : immediate + + if oid.send_redirect?(realm, return_to_uri, immediate) + uri = oid.redirect_url(realm, return_to_uri, immediate) + redirect(uri) + else + confirm_post_params(oid, realm, return_to_uri, immediate) + end + rescue ::OpenID::DiscoveryFailure => e + # thrown from inside OpenID::Consumer#begin by yadis stuff + req.env['rack.errors'].puts([e.message, *e.backtrace]*"\n") + return foreign_server_failure + end + + # This is the final portion of authentication. + # If successful, a redirect to the realm is be returned. + # Data gathered from extensions are stored in session[:openid] with the + # extension's namespace uri as the key. + # + # Any messages from OpenID's response are logged to env['rack.errors'] + # + # env['rack.auth.openid.response'] will contain the openid + # response. + + def finish(consumer, session, req) + oid = consumer.complete(req.GET, req.url) + req.env['rack.auth.openid.response'] = oid + req.env['rack.errors'].puts(oid.message) + p oid if $DEBUG + + raise unless ValidStatus.include?(oid.status) + __send__(oid.status, oid, req, session) + end + + # The first argument should be the main extension module. + # The extension module should contain the constants: + # * class Request, should have OpenID::Extension as an ancestor + # * class Response, should have OpenID::Extension as an ancestor + # * string NS_URI, which defining the namespace of the extension + # + # All trailing arguments will be passed to extension::Request.new in + # #check. + # The openid response will be passed to + # extension::Response#from_success_response, #get_extension_args will be + # called on the result to attain the gathered data. + # + # This method returns the key at which the response data will be found in + # the session, which is the namespace uri by default. + + def add_extension(ext, *args) + raise BadExtension unless valid_extension?(ext) + extensions[ext] = args + return ext::NS_URI + end + + # Checks the validitity, in the context of usage, of a submitted + # extension. + + def valid_extension?(ext) + if not %w[NS_URI Request Response].all?{|c| ext.const_defined?(c) } + raise ArgumentError, 'Extension is missing constants.' + elsif not ext::Response.respond_to?(:from_success_response) + raise ArgumentError, 'Response is missing required method.' + end + return true + rescue + return false + end + + # Checks the provided uri to ensure it'd be considered within the realm. + # is currently not compatible with wildcard realms. + + def within_realm? uri + uri = URI.parse(uri.to_s) + realm = URI.parse(self.realm) + return false unless uri.absolute? + return false unless uri.path[0, realm.path.size] == realm.path + return false unless uri.host == realm.host or realm.host[/^\*\./] + # for wildcard support, is awkward with URI limitations + realm_match = Regexp.escape(realm.host). + sub(/^\*\./,"^#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+.")+'$' + return false unless uri.host.match(realm_match) + return true + end + alias_method :include?, :within_realm? + + protected + + ### These methods define some of the boilerplate responses. + + # Returns an html form page for posting to an Identity Provider if the + # GET request would exceed the upper URI length limit. + + def confirm_post_params(oid, realm, return_to, immediate) + Rack::Response.new.finish do |r| + r.write 'Confirm...' + r.write oid.form_markup(realm, return_to, immediate) + r.write '' + end + end + + # Returns a 303 redirect with the destination of that provided by the + # argument. + + def redirect(uri) + [ 303, {'Content-Length'=>'0', 'Content-Type'=>'text/plain', + 'Location' => uri}, + [] ] + end + + # Returns an empty 400 response. + + def bad_request + [ 400, {'Content-Type'=>'text/plain', 'Content-Length'=>'0'}, + [''] ] + end + + # Returns a basic unauthorized 401 response. + + def unauthorized + [ 401, {'Content-Type' => 'text/plain', 'Content-Length' => '13'}, + ['Unauthorized.'] ] + end + + # Returns a basic access denied 403 response. + + def access_denied + [ 403, {'Content-Type' => 'text/plain', 'Content-Length' => '14'}, + ['Access denied.'] ] + end + + # Returns a 503 response to be used if communication with the remote + # OpenID server fails. + + def foreign_server_failure + [ 503, {'Content-Type'=>'text/plain', 'Content-Length' => '23'}, + ['Foreign server failure.'] ] + end + + private + + ### These methods are called after a transaction is completed, depending + # on its outcome. These should all return a rack compatible response. + # You'd want to override these to provide additional functionality. + + # Called to complete processing on a successful transaction. + # Within the openid session, :openid_identity and :openid_identifier are + # set to the user friendly and the standard representation of the + # validated identity. All other data in the openid session is cleared. + + def success(oid, request, session) + session.clear + session[:openid_identity] = oid.display_identifier + session[:openid_identifier] = oid.identity_url + extensions.keys.each do |ext| + label = ext.name[/[^:]+$/].downcase + response = ext::Response.from_success_response(oid) + session[label] = response.data + end + redirect(realm) + end + + # Called if the Identity Provider indicates further setup by the user is + # required. + # The identifier is retrived from the openid session at :openid_param. + # And :setup_needed is set to true to prevent looping. + + def setup_needed(oid, request, session) + identifier = session[:openid_param] + session[:setup_needed] = true + redirect req.script_name + '?' + openid_param + '=' + identifier + end + + # Called if the user indicates they wish to cancel identification. + # Data within openid session is cleared. + + def cancel(oid, request, session) + session.clear + access_denied + end + + # Called if the Identity Provider indicates the user is unable to confirm + # their identity. Data within the openid session is left alone, in case + # of swarm auth attacks. + + def failure(oid, request, session) + unauthorized + end + end + + # A class developed out of the request to use OpenID as an authentication + # middleware. The request will be sent to the OpenID instance unless the + # block evaluates to true. For example in rackup, you can use it as such: + # + # use Rack::Session::Pool + # use Rack::Auth::OpenIDAuth, realm, openid_options do |env| + # env['rack.session'][:authkey] == a_string + # end + # run RackApp + # + # Or simply: + # + # app = Rack::Auth::OpenIDAuth.new app, realm, openid_options, &auth + + class OpenIDAuth < Rack::Auth::AbstractHandler + attr_reader :oid + def initialize(app, realm, options={}, &auth) + @oid = OpenID.new(realm, options) + super(app, &auth) + end + + def call(env) + to = auth.call(env) ? @app : @oid + to.call env + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/builder.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/builder.rb new file mode 100644 index 0000000..295235e --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/builder.rb @@ -0,0 +1,63 @@ +module Rack + # Rack::Builder implements a small DSL to iteratively construct Rack + # applications. + # + # Example: + # + # app = Rack::Builder.new { + # use Rack::CommonLogger + # use Rack::ShowExceptions + # map "/lobster" do + # use Rack::Lint + # run Rack::Lobster.new + # end + # } + # + # Or + # + # app = Rack::Builder.app do + # use Rack::CommonLogger + # lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] } + # end + # + # +use+ adds a middleware to the stack, +run+ dispatches to an application. + # You can use +map+ to construct a Rack::URLMap in a convenient way. + + class Builder + def initialize(&block) + @ins = [] + instance_eval(&block) if block_given? + end + + def self.app(&block) + self.new(&block).to_app + end + + def use(middleware, *args, &block) + @ins << lambda { |app| middleware.new(app, *args, &block) } + end + + def run(app) + @ins << app #lambda { |nothing| app } + end + + def map(path, &block) + if @ins.last.kind_of? Hash + @ins.last[path] = self.class.new(&block).to_app + else + @ins << {} + map(path, &block) + end + end + + def to_app + @ins[-1] = Rack::URLMap.new(@ins.last) if Hash === @ins.last + inner_app = @ins.last + @ins[0...-1].reverse.inject(inner_app) { |a, e| e.call(a) } + end + + def call(env) + to_app.call(env) + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/cascade.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/cascade.rb new file mode 100644 index 0000000..a038aa1 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/cascade.rb @@ -0,0 +1,36 @@ +module Rack + # Rack::Cascade tries an request on several apps, and returns the + # first response that is not 404 (or in a list of configurable + # status codes). + + class Cascade + attr_reader :apps + + def initialize(apps, catch=404) + @apps = apps + @catch = [*catch] + end + + def call(env) + status = headers = body = nil + raise ArgumentError, "empty cascade" if @apps.empty? + @apps.each { |app| + begin + status, headers, body = app.call(env) + break unless @catch.include?(status.to_i) + end + } + [status, headers, body] + end + + def add app + @apps << app + end + + def include? app + @apps.include? app + end + + alias_method :<<, :add + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/chunked.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/chunked.rb new file mode 100644 index 0000000..280d89d --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/chunked.rb @@ -0,0 +1,49 @@ +require 'rack/utils' + +module Rack + + # Middleware that applies chunked transfer encoding to response bodies + # when the response does not include a Content-Length header. + class Chunked + include Rack::Utils + + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + headers = HeaderHash.new(headers) + + if env['HTTP_VERSION'] == 'HTTP/1.0' || + STATUS_WITH_NO_ENTITY_BODY.include?(status) || + headers['Content-Length'] || + headers['Transfer-Encoding'] + [status, headers.to_hash, body] + else + dup.chunk(status, headers, body) + end + end + + def chunk(status, headers, body) + @body = body + headers.delete('Content-Length') + headers['Transfer-Encoding'] = 'chunked' + [status, headers.to_hash, self] + end + + def each + term = "\r\n" + @body.each do |chunk| + size = bytesize(chunk) + next if size == 0 + yield [size.to_s(16), term, chunk, term].join + end + yield ["0", term, "", term].join + end + + def close + @body.close if @body.respond_to?(:close) + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/commonlogger.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/commonlogger.rb new file mode 100644 index 0000000..5e68ac6 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/commonlogger.rb @@ -0,0 +1,61 @@ +module Rack + # Rack::CommonLogger forwards every request to an +app+ given, and + # logs a line in the Apache common log format to the +logger+, or + # rack.errors by default. + + class CommonLogger + def initialize(app, logger=nil) + @app = app + @logger = logger + end + + def call(env) + dup._call(env) + end + + def _call(env) + @env = env + @logger ||= self + @time = Time.now + @status, @header, @body = @app.call(env) + [@status, @header, self] + end + + def close + @body.close if @body.respond_to? :close + end + + # By default, log to rack.errors. + def <<(str) + @env["rack.errors"].write(str) + @env["rack.errors"].flush + end + + def each + length = 0 + @body.each { |part| + length += part.size + yield part + } + + @now = Time.now + + # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common + # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 - + # %{%s - %s [%s] "%s %s%s %s" %d %s\n} % + @logger << %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} % + [ + @env['HTTP_X_FORWARDED_FOR'] || @env["REMOTE_ADDR"] || "-", + @env["REMOTE_USER"] || "-", + @now.strftime("%d/%b/%Y %H:%M:%S"), + @env["REQUEST_METHOD"], + @env["PATH_INFO"], + @env["QUERY_STRING"].empty? ? "" : "?"+@env["QUERY_STRING"], + @env["HTTP_VERSION"], + @status.to_s[0..3], + (length.zero? ? "-" : length.to_s), + @now - @time + ] + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/conditionalget.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/conditionalget.rb new file mode 100644 index 0000000..046ebdb --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/conditionalget.rb @@ -0,0 +1,47 @@ +require 'rack/utils' + +module Rack + + # Middleware that enables conditional GET using If-None-Match and + # If-Modified-Since. The application should set either or both of the + # Last-Modified or Etag response headers according to RFC 2616. When + # either of the conditions is met, the response body is set to be zero + # length and the response status is set to 304 Not Modified. + # + # Applications that defer response body generation until the body's each + # message is received will avoid response body generation completely when + # a conditional GET matches. + # + # Adapted from Michael Klishin's Merb implementation: + # http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb + class ConditionalGet + def initialize(app) + @app = app + end + + def call(env) + return @app.call(env) unless %w[GET HEAD].include?(env['REQUEST_METHOD']) + + status, headers, body = @app.call(env) + headers = Utils::HeaderHash.new(headers) + if etag_matches?(env, headers) || modified_since?(env, headers) + status = 304 + headers.delete('Content-Type') + headers.delete('Content-Length') + body = [] + end + [status, headers, body] + end + + private + def etag_matches?(env, headers) + etag = headers['Etag'] and etag == env['HTTP_IF_NONE_MATCH'] + end + + def modified_since?(env, headers) + last_modified = headers['Last-Modified'] and + last_modified == env['HTTP_IF_MODIFIED_SINCE'] + end + end + +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_length.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_length.rb new file mode 100644 index 0000000..1e56d43 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_length.rb @@ -0,0 +1,29 @@ +require 'rack/utils' + +module Rack + # Sets the Content-Length header on responses with fixed-length bodies. + class ContentLength + include Rack::Utils + + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + headers = HeaderHash.new(headers) + + if !STATUS_WITH_NO_ENTITY_BODY.include?(status) && + !headers['Content-Length'] && + !headers['Transfer-Encoding'] && + (body.respond_to?(:to_ary) || body.respond_to?(:to_str)) + + body = [body] if body.respond_to?(:to_str) # rack 0.4 compat + length = body.to_ary.inject(0) { |len, part| len + bytesize(part) } + headers['Content-Length'] = length.to_s + end + + [status, headers, body] + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_type.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_type.rb new file mode 100644 index 0000000..0c1e1ca --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_type.rb @@ -0,0 +1,23 @@ +require 'rack/utils' + +module Rack + + # Sets the Content-Type header on responses which don't have one. + # + # Builder Usage: + # use Rack::ContentType, "text/plain" + # + # When no content type argument is provided, "text/html" is assumed. + class ContentType + def initialize(app, content_type = "text/html") + @app, @content_type = app, content_type + end + + def call(env) + status, headers, body = @app.call(env) + headers = Utils::HeaderHash.new(headers) + headers['Content-Type'] ||= @content_type + [status, headers.to_hash, body] + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/deflater.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/deflater.rb new file mode 100644 index 0000000..14137a9 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/deflater.rb @@ -0,0 +1,96 @@ +require "zlib" +require "stringio" +require "time" # for Time.httpdate +require 'rack/utils' + +module Rack + class Deflater + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + headers = Utils::HeaderHash.new(headers) + + # Skip compressing empty entity body responses and responses with + # no-transform set. + if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) || + headers['Cache-Control'].to_s =~ /\bno-transform\b/ + return [status, headers, body] + end + + request = Request.new(env) + + encoding = Utils.select_best_encoding(%w(gzip deflate identity), + request.accept_encoding) + + # Set the Vary HTTP header. + vary = headers["Vary"].to_s.split(",").map { |v| v.strip } + unless vary.include?("*") || vary.include?("Accept-Encoding") + headers["Vary"] = vary.push("Accept-Encoding").join(",") + end + + case encoding + when "gzip" + headers['Content-Encoding'] = "gzip" + headers.delete('Content-Length') + mtime = headers.key?("Last-Modified") ? + Time.httpdate(headers["Last-Modified"]) : Time.now + [status, headers, GzipStream.new(body, mtime)] + when "deflate" + headers['Content-Encoding'] = "deflate" + headers.delete('Content-Length') + [status, headers, DeflateStream.new(body)] + when "identity" + [status, headers, body] + when nil + message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found." + [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]] + end + end + + class GzipStream + def initialize(body, mtime) + @body = body + @mtime = mtime + end + + def each(&block) + @writer = block + gzip =::Zlib::GzipWriter.new(self) + gzip.mtime = @mtime + @body.each { |part| gzip << part } + @body.close if @body.respond_to?(:close) + gzip.close + @writer = nil + end + + def write(data) + @writer.call(data) + end + end + + class DeflateStream + DEFLATE_ARGS = [ + Zlib::DEFAULT_COMPRESSION, + # drop the zlib header which causes both Safari and IE to choke + -Zlib::MAX_WBITS, + Zlib::DEF_MEM_LEVEL, + Zlib::DEFAULT_STRATEGY + ] + + def initialize(body) + @body = body + end + + def each + deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS) + @body.each { |part| yield deflater.deflate(part) } + @body.close if @body.respond_to?(:close) + yield deflater.finish + nil + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/directory.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/directory.rb new file mode 100644 index 0000000..acdd302 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/directory.rb @@ -0,0 +1,153 @@ +require 'time' +require 'rack/utils' +require 'rack/mime' + +module Rack + # Rack::Directory serves entries below the +root+ given, according to the + # path info of the Rack request. If a directory is found, the file's contents + # will be presented in an html based index. If a file is found, the env will + # be passed to the specified +app+. + # + # If +app+ is not specified, a Rack::File of the same +root+ will be used. + + class Directory + DIR_FILE = "%s%s%s%s" + DIR_PAGE = <<-PAGE + + %s + + + +

%s

+
+ + + + + + + +%s +
NameSizeTypeLast Modified
+
+ + PAGE + + attr_reader :files + attr_accessor :root, :path + + def initialize(root, app=nil) + @root = F.expand_path(root) + @app = app || Rack::File.new(@root) + end + + def call(env) + dup._call(env) + end + + F = ::File + + def _call(env) + @env = env + @script_name = env['SCRIPT_NAME'] + @path_info = Utils.unescape(env['PATH_INFO']) + + if forbidden = check_forbidden + forbidden + else + @path = F.join(@root, @path_info) + list_path + end + end + + def check_forbidden + return unless @path_info.include? ".." + + body = "Forbidden\n" + size = Rack::Utils.bytesize(body) + return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]] + end + + def list_directory + @files = [['../','Parent Directory','','','']] + glob = F.join(@path, '*') + + Dir[glob].sort.each do |node| + stat = stat(node) + next unless stat + basename = F.basename(node) + ext = F.extname(node) + + url = F.join(@script_name, @path_info, basename) + size = stat.size + type = stat.directory? ? 'directory' : Mime.mime_type(ext) + size = stat.directory? ? '-' : filesize_format(size) + mtime = stat.mtime.httpdate + url << '/' if stat.directory? + basename << '/' if stat.directory? + + @files << [ url, basename, size, type, mtime ] + end + + return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ] + end + + def stat(node, max = 10) + F.stat(node) + rescue Errno::ENOENT, Errno::ELOOP + return nil + end + + # TODO: add correct response if not readable, not sure if 404 is the best + # option + def list_path + @stat = F.stat(@path) + + if @stat.readable? + return @app.call(@env) if @stat.file? + return list_directory if @stat.directory? + else + raise Errno::ENOENT, 'No such file or directory' + end + + rescue Errno::ENOENT, Errno::ELOOP + return entity_not_found + end + + def entity_not_found + body = "Entity not found: #{@path_info}\n" + size = Rack::Utils.bytesize(body) + return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]] + end + + def each + show_path = @path.sub(/^#{@root}/,'') + files = @files.map{|f| DIR_FILE % f }*"\n" + page = DIR_PAGE % [ show_path, show_path , files ] + page.each_line{|l| yield l } + end + + # Stolen from Ramaze + + FILESIZE_FORMAT = [ + ['%.1fT', 1 << 40], + ['%.1fG', 1 << 30], + ['%.1fM', 1 << 20], + ['%.1fK', 1 << 10], + ] + + def filesize_format(int) + FILESIZE_FORMAT.each do |format, size| + return format % (int.to_f / size) if int >= size + end + + int.to_s + 'B' + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/file.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/file.rb new file mode 100644 index 0000000..fe62bd6 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/file.rb @@ -0,0 +1,88 @@ +require 'time' +require 'rack/utils' +require 'rack/mime' + +module Rack + # Rack::File serves files below the +root+ given, according to the + # path info of the Rack request. + # + # Handlers can detect if bodies are a Rack::File, and use mechanisms + # like sendfile on the +path+. + + class File + attr_accessor :root + attr_accessor :path + + alias :to_path :path + + def initialize(root) + @root = root + end + + def call(env) + dup._call(env) + end + + F = ::File + + def _call(env) + @path_info = Utils.unescape(env["PATH_INFO"]) + return forbidden if @path_info.include? ".." + + @path = F.join(@root, @path_info) + + begin + if F.file?(@path) && F.readable?(@path) + serving + else + raise Errno::EPERM + end + rescue SystemCallError + not_found + end + end + + def forbidden + body = "Forbidden\n" + [403, {"Content-Type" => "text/plain", + "Content-Length" => body.size.to_s}, + [body]] + end + + # NOTE: + # We check via File::size? whether this file provides size info + # via stat (e.g. /proc files often don't), otherwise we have to + # figure it out by reading the whole file into memory. And while + # we're at it we also use this as body then. + + def serving + if size = F.size?(@path) + body = self + else + body = [F.read(@path)] + size = Utils.bytesize(body.first) + end + + [200, { + "Last-Modified" => F.mtime(@path).httpdate, + "Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain'), + "Content-Length" => size.to_s + }, body] + end + + def not_found + body = "File not found: #{@path_info}\n" + [404, {"Content-Type" => "text/plain", + "Content-Length" => body.size.to_s}, + [body]] + end + + def each + F.open(@path, "rb") { |file| + while part = file.read(8192) + yield part + end + } + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler.rb new file mode 100644 index 0000000..5624a1e --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler.rb @@ -0,0 +1,69 @@ +module Rack + # *Handlers* connect web servers with Rack. + # + # Rack includes Handlers for Mongrel, WEBrick, FastCGI, CGI, SCGI + # and LiteSpeed. + # + # Handlers usually are activated by calling MyHandler.run(myapp). + # A second optional hash can be passed to include server-specific + # configuration. + module Handler + def self.get(server) + return unless server + server = server.to_s + + if klass = @handlers[server] + obj = Object + klass.split("::").each { |x| obj = obj.const_get(x) } + obj + else + try_require('rack/handler', server) + const_get(server) + end + end + + # Transforms server-name constants to their canonical form as filenames, + # then tries to require them but silences the LoadError if not found + # + # Naming convention: + # + # Foo # => 'foo' + # FooBar # => 'foo_bar.rb' + # FooBAR # => 'foobar.rb' + # FOObar # => 'foobar.rb' + # FOOBAR # => 'foobar.rb' + # FooBarBaz # => 'foo_bar_baz.rb' + def self.try_require(prefix, const_name) + file = const_name.gsub(/^[A-Z]+/) { |pre| pre.downcase }. + gsub(/[A-Z]+[^A-Z]/, '_\&').downcase + + require(::File.join(prefix, file)) + rescue LoadError + end + + def self.register(server, klass) + @handlers ||= {} + @handlers[server] = klass + end + + autoload :CGI, "rack/handler/cgi" + autoload :FastCGI, "rack/handler/fastcgi" + autoload :Mongrel, "rack/handler/mongrel" + autoload :EventedMongrel, "rack/handler/evented_mongrel" + autoload :SwiftipliedMongrel, "rack/handler/swiftiplied_mongrel" + autoload :WEBrick, "rack/handler/webrick" + autoload :LSWS, "rack/handler/lsws" + autoload :SCGI, "rack/handler/scgi" + autoload :Thin, "rack/handler/thin" + + register 'cgi', 'Rack::Handler::CGI' + register 'fastcgi', 'Rack::Handler::FastCGI' + register 'mongrel', 'Rack::Handler::Mongrel' + register 'emongrel', 'Rack::Handler::EventedMongrel' + register 'smongrel', 'Rack::Handler::SwiftipliedMongrel' + register 'webrick', 'Rack::Handler::WEBrick' + register 'lsws', 'Rack::Handler::LSWS' + register 'scgi', 'Rack::Handler::SCGI' + register 'thin', 'Rack::Handler::Thin' + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/cgi.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/cgi.rb new file mode 100644 index 0000000..f45f3d7 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/cgi.rb @@ -0,0 +1,61 @@ +require 'rack/content_length' + +module Rack + module Handler + class CGI + def self.run(app, options=nil) + serve app + end + + def self.serve(app) + app = ContentLength.new(app) + + env = ENV.to_hash + env.delete "HTTP_CONTENT_LENGTH" + + env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" + + env.update({"rack.version" => [1,0], + "rack.input" => $stdin, + "rack.errors" => $stderr, + + "rack.multithread" => false, + "rack.multiprocess" => true, + "rack.run_once" => true, + + "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" + }) + + env["QUERY_STRING"] ||= "" + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["REQUEST_PATH"] ||= "/" + + status, headers, body = app.call(env) + begin + send_headers status, headers + send_body body + ensure + body.close if body.respond_to? :close + end + end + + def self.send_headers(status, headers) + STDOUT.print "Status: #{status}\r\n" + headers.each { |k, vs| + vs.split("\n").each { |v| + STDOUT.print "#{k}: #{v}\r\n" + } + } + STDOUT.print "\r\n" + STDOUT.flush + end + + def self.send_body(body) + body.each { |part| + STDOUT.print part + STDOUT.flush + } + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb new file mode 100644 index 0000000..0f5cbf7 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb @@ -0,0 +1,8 @@ +require 'swiftcore/evented_mongrel' + +module Rack + module Handler + class EventedMongrel < Handler::Mongrel + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/fastcgi.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/fastcgi.rb new file mode 100644 index 0000000..2cbb502 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/fastcgi.rb @@ -0,0 +1,88 @@ +require 'fcgi' +require 'socket' +require 'rack/content_length' +require 'rack/rewindable_input' + +class FCGI::Stream + alias _rack_read_without_buffer read + + def read(n, buffer=nil) + buf = _rack_read_without_buffer n + buffer.replace(buf.to_s) if buffer + buf + end +end + +module Rack + module Handler + class FastCGI + def self.run(app, options={}) + file = options[:File] and STDIN.reopen(UNIXServer.new(file)) + port = options[:Port] and STDIN.reopen(TCPServer.new(port)) + FCGI.each { |request| + serve request, app + } + end + + def self.serve(request, app) + app = Rack::ContentLength.new(app) + + env = request.env + env.delete "HTTP_CONTENT_LENGTH" + + env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" + + rack_input = RewindableInput.new(request.in) + + env.update({"rack.version" => [1,0], + "rack.input" => rack_input, + "rack.errors" => request.err, + + "rack.multithread" => false, + "rack.multiprocess" => true, + "rack.run_once" => false, + + "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" + }) + + env["QUERY_STRING"] ||= "" + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["REQUEST_PATH"] ||= "/" + env.delete "PATH_INFO" if env["PATH_INFO"] == "" + env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == "" + env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == "" + + begin + status, headers, body = app.call(env) + begin + send_headers request.out, status, headers + send_body request.out, body + ensure + body.close if body.respond_to? :close + end + ensure + rack_input.close + request.finish + end + end + + def self.send_headers(out, status, headers) + out.print "Status: #{status}\r\n" + headers.each { |k, vs| + vs.split("\n").each { |v| + out.print "#{k}: #{v}\r\n" + } + } + out.print "\r\n" + out.flush + end + + def self.send_body(out, body) + body.each { |part| + out.print part + out.flush + } + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/lsws.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/lsws.rb new file mode 100644 index 0000000..7231336 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/lsws.rb @@ -0,0 +1,55 @@ +require 'lsapi' +require 'rack/content_length' + +module Rack + module Handler + class LSWS + def self.run(app, options=nil) + while LSAPI.accept != nil + serve app + end + end + def self.serve(app) + app = Rack::ContentLength.new(app) + + env = ENV.to_hash + env.delete "HTTP_CONTENT_LENGTH" + env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" + env.update({"rack.version" => [1,0], + "rack.input" => StringIO.new($stdin.read.to_s), + "rack.errors" => $stderr, + "rack.multithread" => false, + "rack.multiprocess" => true, + "rack.run_once" => false, + "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" + }) + env["QUERY_STRING"] ||= "" + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["REQUEST_PATH"] ||= "/" + status, headers, body = app.call(env) + begin + send_headers status, headers + send_body body + ensure + body.close if body.respond_to? :close + end + end + def self.send_headers(status, headers) + print "Status: #{status}\r\n" + headers.each { |k, vs| + vs.split("\n").each { |v| + print "#{k}: #{v}\r\n" + } + } + print "\r\n" + STDOUT.flush + end + def self.send_body(body) + body.each { |part| + print part + STDOUT.flush + } + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/mongrel.rb new file mode 100644 index 0000000..a6b4fa9 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/mongrel.rb @@ -0,0 +1,84 @@ +require 'mongrel' +require 'stringio' +require 'rack/content_length' +require 'rack/chunked' + +module Rack + module Handler + class Mongrel < ::Mongrel::HttpHandler + def self.run(app, options={}) + server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0', + options[:Port] || 8080) + # Acts like Rack::URLMap, utilizing Mongrel's own path finding methods. + # Use is similar to #run, replacing the app argument with a hash of + # { path=>app, ... } or an instance of Rack::URLMap. + if options[:map] + if app.is_a? Hash + app.each do |path, appl| + path = '/'+path unless path[0] == ?/ + server.register(path, Rack::Handler::Mongrel.new(appl)) + end + elsif app.is_a? URLMap + app.instance_variable_get(:@mapping).each do |(host, path, appl)| + next if !host.nil? && !options[:Host].nil? && options[:Host] != host + path = '/'+path unless path[0] == ?/ + server.register(path, Rack::Handler::Mongrel.new(appl)) + end + else + raise ArgumentError, "first argument should be a Hash or URLMap" + end + else + server.register('/', Rack::Handler::Mongrel.new(app)) + end + yield server if block_given? + server.run.join + end + + def initialize(app) + @app = Rack::Chunked.new(Rack::ContentLength.new(app)) + end + + def process(request, response) + env = {}.replace(request.params) + env.delete "HTTP_CONTENT_TYPE" + env.delete "HTTP_CONTENT_LENGTH" + + env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" + + env.update({"rack.version" => [1,0], + "rack.input" => request.body || StringIO.new(""), + "rack.errors" => $stderr, + + "rack.multithread" => true, + "rack.multiprocess" => false, # ??? + "rack.run_once" => false, + + "rack.url_scheme" => "http", + }) + env["QUERY_STRING"] ||= "" + env.delete "PATH_INFO" if env["PATH_INFO"] == "" + + status, headers, body = @app.call(env) + + begin + response.status = status.to_i + response.send_status(nil) + + headers.each { |k, vs| + vs.split("\n").each { |v| + response.header[k] = v + } + } + response.send_header + + body.each { |part| + response.write part + response.socket.flush + } + ensure + body.close if body.respond_to? :close + end + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/scgi.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/scgi.rb new file mode 100644 index 0000000..df0e764 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/scgi.rb @@ -0,0 +1,59 @@ +require 'scgi' +require 'stringio' +require 'rack/content_length' +require 'rack/chunked' + +module Rack + module Handler + class SCGI < ::SCGI::Processor + attr_accessor :app + + def self.run(app, options=nil) + new(options.merge(:app=>app, + :host=>options[:Host], + :port=>options[:Port], + :socket=>options[:Socket])).listen + end + + def initialize(settings = {}) + @app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app])) + @log = Object.new + def @log.info(*args); end + def @log.error(*args); end + super(settings) + end + + def process_request(request, input_body, socket) + env = {}.replace(request) + env.delete "HTTP_CONTENT_TYPE" + env.delete "HTTP_CONTENT_LENGTH" + env["REQUEST_PATH"], env["QUERY_STRING"] = env["REQUEST_URI"].split('?', 2) + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["PATH_INFO"] = env["REQUEST_PATH"] + env["QUERY_STRING"] ||= "" + env["SCRIPT_NAME"] = "" + env.update({"rack.version" => [1,0], + "rack.input" => StringIO.new(input_body), + "rack.errors" => $stderr, + + "rack.multithread" => true, + "rack.multiprocess" => true, + "rack.run_once" => false, + + "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" + }) + status, headers, body = app.call(env) + begin + socket.write("Status: #{status}\r\n") + headers.each do |k, vs| + vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")} + end + socket.write("\r\n") + body.each {|s| socket.write(s)} + ensure + body.close if body.respond_to? :close + end + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb new file mode 100644 index 0000000..4bafd0b --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb @@ -0,0 +1,8 @@ +require 'swiftcore/swiftiplied_mongrel' + +module Rack + module Handler + class SwiftipliedMongrel < Handler::Mongrel + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/thin.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/thin.rb new file mode 100644 index 0000000..3d4fedf --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/thin.rb @@ -0,0 +1,18 @@ +require "thin" +require "rack/content_length" +require "rack/chunked" + +module Rack + module Handler + class Thin + def self.run(app, options={}) + app = Rack::Chunked.new(Rack::ContentLength.new(app)) + server = ::Thin::Server.new(options[:Host] || '0.0.0.0', + options[:Port] || 8080, + app) + yield server if block_given? + server.start + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/webrick.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/webrick.rb new file mode 100644 index 0000000..2bdc83a --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/webrick.rb @@ -0,0 +1,67 @@ +require 'webrick' +require 'stringio' +require 'rack/content_length' + +module Rack + module Handler + class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet + def self.run(app, options={}) + server = ::WEBrick::HTTPServer.new(options) + server.mount "/", Rack::Handler::WEBrick, app + trap(:INT) { server.shutdown } + yield server if block_given? + server.start + end + + def initialize(server, app) + super server + @app = Rack::ContentLength.new(app) + end + + def service(req, res) + env = req.meta_vars + env.delete_if { |k, v| v.nil? } + + env.update({"rack.version" => [1,0], + "rack.input" => StringIO.new(req.body.to_s), + "rack.errors" => $stderr, + + "rack.multithread" => true, + "rack.multiprocess" => false, + "rack.run_once" => false, + + "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" + }) + + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["QUERY_STRING"] ||= "" + env["REQUEST_PATH"] ||= "/" + if env["PATH_INFO"] == "" + env.delete "PATH_INFO" + else + path, n = req.request_uri.path, env["SCRIPT_NAME"].length + env["PATH_INFO"] = path[n, path.length-n] + end + + status, headers, body = @app.call(env) + begin + res.status = status.to_i + headers.each { |k, vs| + if k.downcase == "set-cookie" + res.cookies.concat vs.split("\n") + else + vs.split("\n").each { |v| + res[k] = v + } + end + } + body.each { |part| + res.body << part + } + ensure + body.close if body.respond_to? :close + end + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/head.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/head.rb new file mode 100644 index 0000000..deab822 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/head.rb @@ -0,0 +1,19 @@ +module Rack + +class Head + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + + if env["REQUEST_METHOD"] == "HEAD" + [status, headers, []] + else + [status, headers, body] + end + end +end + +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lint.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lint.rb new file mode 100644 index 0000000..bb0693d --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lint.rb @@ -0,0 +1,537 @@ +require 'rack/utils' + +module Rack + # Rack::Lint validates your application and the requests and + # responses according to the Rack spec. + + class Lint + def initialize(app) + @app = app + end + + # :stopdoc: + + class LintError < RuntimeError; end + module Assertion + def assert(message, &block) + unless block.call + raise LintError, message + end + end + end + include Assertion + + ## This specification aims to formalize the Rack protocol. You + ## can (and should) use Rack::Lint to enforce it. + ## + ## When you develop middleware, be sure to add a Lint before and + ## after to catch all mistakes. + + ## = Rack applications + + ## A Rack application is an Ruby object (not a class) that + ## responds to +call+. + def call(env=nil) + dup._call(env) + end + + def _call(env) + ## It takes exactly one argument, the *environment* + assert("No env given") { env } + check_env env + + env['rack.input'] = InputWrapper.new(env['rack.input']) + env['rack.errors'] = ErrorWrapper.new(env['rack.errors']) + + ## and returns an Array of exactly three values: + status, headers, @body = @app.call(env) + ## The *status*, + check_status status + ## the *headers*, + check_headers headers + ## and the *body*. + check_content_type status, headers + check_content_length status, headers, env + [status, headers, self] + end + + ## == The Environment + def check_env(env) + ## The environment must be an true instance of Hash (no + ## subclassing allowed) that includes CGI-like headers. + ## The application is free to modify the environment. + assert("env #{env.inspect} is not a Hash, but #{env.class}") { + env.instance_of? Hash + } + + ## + ## The environment is required to include these variables + ## (adopted from PEP333), except when they'd be empty, but see + ## below. + + ## REQUEST_METHOD:: The HTTP request method, such as + ## "GET" or "POST". This cannot ever + ## be an empty string, and so is + ## always required. + + ## SCRIPT_NAME:: The initial portion of the request + ## URL's "path" that corresponds to the + ## application object, so that the + ## application knows its virtual + ## "location". This may be an empty + ## string, if the application corresponds + ## to the "root" of the server. + + ## PATH_INFO:: The remainder of the request URL's + ## "path", designating the virtual + ## "location" of the request's target + ## within the application. This may be an + ## empty string, if the request URL targets + ## the application root and does not have a + ## trailing slash. This value may be + ## percent-encoded when I originating from + ## a URL. + + ## QUERY_STRING:: The portion of the request URL that + ## follows the ?, if any. May be + ## empty, but is always required! + + ## SERVER_NAME, SERVER_PORT:: When combined with SCRIPT_NAME and PATH_INFO, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to SERVER_NAME for reconstructing the request URL. SERVER_NAME and SERVER_PORT can never be empty strings, and so are always required. + + ## HTTP_ Variables:: Variables corresponding to the + ## client-supplied HTTP request + ## headers (i.e., variables whose + ## names begin with HTTP_). The + ## presence or absence of these + ## variables should correspond with + ## the presence or absence of the + ## appropriate HTTP header in the + ## request. + + ## In addition to this, the Rack environment must include these + ## Rack-specific variables: + + ## rack.version:: The Array [1,0], representing this version of Rack. + ## rack.url_scheme:: +http+ or +https+, depending on the request URL. + ## rack.input:: See below, the input stream. + ## rack.errors:: See below, the error stream. + ## rack.multithread:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise. + ## rack.multiprocess:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise. + ## rack.run_once:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar). + ## + + ## Additional environment specifications have approved to + ## standardized middleware APIs. None of these are required to + ## be implemented by the server. + + ## rack.session:: A hash like interface for storing request session data. + ## The store must implement: + if session = env['rack.session'] + ## store(key, value) (aliased as []=); + assert("session #{session.inspect} must respond to store and []=") { + session.respond_to?(:store) && session.respond_to?(:[]=) + } + + ## fetch(key, default = nil) (aliased as []); + assert("session #{session.inspect} must respond to fetch and []") { + session.respond_to?(:fetch) && session.respond_to?(:[]) + } + + ## delete(key); + assert("session #{session.inspect} must respond to delete") { + session.respond_to?(:delete) + } + + ## clear; + assert("session #{session.inspect} must respond to clear") { + session.respond_to?(:clear) + } + end + + ## The server or the application can store their own data in the + ## environment, too. The keys must contain at least one dot, + ## and should be prefixed uniquely. The prefix rack. + ## is reserved for use with the Rack core distribution and other + ## accepted specifications and must not be used otherwise. + ## + + %w[REQUEST_METHOD SERVER_NAME SERVER_PORT + QUERY_STRING + rack.version rack.input rack.errors + rack.multithread rack.multiprocess rack.run_once].each { |header| + assert("env missing required key #{header}") { env.include? header } + } + + ## The environment must not contain the keys + ## HTTP_CONTENT_TYPE or HTTP_CONTENT_LENGTH + ## (use the versions without HTTP_). + %w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header| + assert("env contains #{header}, must use #{header[5,-1]}") { + not env.include? header + } + } + + ## The CGI keys (named without a period) must have String values. + env.each { |key, value| + next if key.include? "." # Skip extensions + assert("env variable #{key} has non-string value #{value.inspect}") { + value.instance_of? String + } + } + + ## + ## There are the following restrictions: + + ## * rack.version must be an array of Integers. + assert("rack.version must be an Array, was #{env["rack.version"].class}") { + env["rack.version"].instance_of? Array + } + ## * rack.url_scheme must either be +http+ or +https+. + assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") { + %w[http https].include? env["rack.url_scheme"] + } + + ## * There must be a valid input stream in rack.input. + check_input env["rack.input"] + ## * There must be a valid error stream in rack.errors. + check_error env["rack.errors"] + + ## * The REQUEST_METHOD must be a valid token. + assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") { + env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/ + } + + ## * The SCRIPT_NAME, if non-empty, must start with / + assert("SCRIPT_NAME must start with /") { + !env.include?("SCRIPT_NAME") || + env["SCRIPT_NAME"] == "" || + env["SCRIPT_NAME"] =~ /\A\// + } + ## * The PATH_INFO, if non-empty, must start with / + assert("PATH_INFO must start with /") { + !env.include?("PATH_INFO") || + env["PATH_INFO"] == "" || + env["PATH_INFO"] =~ /\A\// + } + ## * The CONTENT_LENGTH, if given, must consist of digits only. + assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") { + !env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/ + } + + ## * One of SCRIPT_NAME or PATH_INFO must be + ## set. PATH_INFO should be / if + ## SCRIPT_NAME is empty. + assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") { + env["SCRIPT_NAME"] || env["PATH_INFO"] + } + ## SCRIPT_NAME never should be /, but instead be empty. + assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") { + env["SCRIPT_NAME"] != "/" + } + end + + ## === The Input Stream + ## + ## The input stream is an IO-like object which contains the raw HTTP + ## POST data. If it is a file then it must be opened in binary mode. + def check_input(input) + ## The input stream must respond to +gets+, +each+, +read+ and +rewind+. + [:gets, :each, :read, :rewind].each { |method| + assert("rack.input #{input} does not respond to ##{method}") { + input.respond_to? method + } + } + end + + class InputWrapper + include Assertion + + def initialize(input) + @input = input + end + + def size + @input.size + end + + ## * +gets+ must be called without arguments and return a string, + ## or +nil+ on EOF. + def gets(*args) + assert("rack.input#gets called with arguments") { args.size == 0 } + v = @input.gets + assert("rack.input#gets didn't return a String") { + v.nil? or v.instance_of? String + } + v + end + + ## * +read+ behaves like IO#read. Its signature is read([length, [buffer]]). + ## If given, +length+ must be an non-negative Integer (>= 0) or +nil+, and +buffer+ must + ## be a String and may not be nil. If +length+ is given and not nil, then this method + ## reads at most +length+ bytes from the input stream. If +length+ is not given or nil, + ## then this method reads all data until EOF. + ## When EOF is reached, this method returns nil if +length+ is given and not nil, or "" + ## if +length+ is not given or is nil. + ## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a + ## newly created String object. + def read(*args) + assert("rack.input#read called with too many arguments") { + args.size <= 2 + } + if args.size >= 1 + assert("rack.input#read called with non-integer and non-nil length") { + args.first.kind_of?(Integer) || args.first.nil? + } + assert("rack.input#read called with a negative length") { + args.first.nil? || args.first >= 0 + } + end + if args.size >= 2 + assert("rack.input#read called with non-String buffer") { + args[1].kind_of?(String) + } + end + + v = @input.read(*args) + + assert("rack.input#read didn't return nil or a String") { + v.nil? or v.instance_of? String + } + if args[0].nil? + assert("rack.input#read(nil) returned nil on EOF") { + !v.nil? + } + end + + v + end + + ## * +each+ must be called without arguments and only yield Strings. + def each(*args) + assert("rack.input#each called with arguments") { args.size == 0 } + @input.each { |line| + assert("rack.input#each didn't yield a String") { + line.instance_of? String + } + yield line + } + end + + ## * +rewind+ must be called without arguments. It rewinds the input + ## stream back to the beginning. It must not raise Errno::ESPIPE: + ## that is, it may not be a pipe or a socket. Therefore, handler + ## developers must buffer the input data into some rewindable object + ## if the underlying input stream is not rewindable. + def rewind(*args) + assert("rack.input#rewind called with arguments") { args.size == 0 } + assert("rack.input#rewind raised Errno::ESPIPE") { + begin + @input.rewind + true + rescue Errno::ESPIPE + false + end + } + end + + ## * +close+ must never be called on the input stream. + def close(*args) + assert("rack.input#close must not be called") { false } + end + end + + ## === The Error Stream + def check_error(error) + ## The error stream must respond to +puts+, +write+ and +flush+. + [:puts, :write, :flush].each { |method| + assert("rack.error #{error} does not respond to ##{method}") { + error.respond_to? method + } + } + end + + class ErrorWrapper + include Assertion + + def initialize(error) + @error = error + end + + ## * +puts+ must be called with a single argument that responds to +to_s+. + def puts(str) + @error.puts str + end + + ## * +write+ must be called with a single argument that is a String. + def write(str) + assert("rack.errors#write not called with a String") { str.instance_of? String } + @error.write str + end + + ## * +flush+ must be called without arguments and must be called + ## in order to make the error appear for sure. + def flush + @error.flush + end + + ## * +close+ must never be called on the error stream. + def close(*args) + assert("rack.errors#close must not be called") { false } + end + end + + ## == The Response + + ## === The Status + def check_status(status) + ## This is an HTTP status. When parsed as integer (+to_i+), it must be + ## greater than or equal to 100. + assert("Status must be >=100 seen as integer") { status.to_i >= 100 } + end + + ## === The Headers + def check_headers(header) + ## The header must respond to +each+, and yield values of key and value. + assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") { + header.respond_to? :each + } + header.each { |key, value| + ## The header keys must be Strings. + assert("header key must be a string, was #{key.class}") { + key.instance_of? String + } + ## The header must not contain a +Status+ key, + assert("header must not contain Status") { key.downcase != "status" } + ## contain keys with : or newlines in their name, + assert("header names must not contain : or \\n") { key !~ /[:\n]/ } + ## contain keys names that end in - or _, + assert("header names must not end in - or _") { key !~ /[-_]\z/ } + ## but only contain keys that consist of + ## letters, digits, _ or - and start with a letter. + assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ } + + ## The values of the header must be Strings, + assert("a header value must be a String, but the value of " + + "'#{key}' is a #{value.class}") { value.kind_of? String } + ## consisting of lines (for multiple header values, e.g. multiple + ## Set-Cookie values) seperated by "\n". + value.split("\n").each { |item| + ## The lines must not contain characters below 037. + assert("invalid header value #{key}: #{item.inspect}") { + item !~ /[\000-\037]/ + } + } + } + end + + ## === The Content-Type + def check_content_type(status, headers) + headers.each { |key, value| + ## There must be a Content-Type, except when the + ## +Status+ is 1xx, 204 or 304, in which case there must be none + ## given. + if key.downcase == "content-type" + assert("Content-Type header found in #{status} response, not allowed") { + not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i + } + return + end + } + assert("No Content-Type header found") { + Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i + } + end + + ## === The Content-Length + def check_content_length(status, headers, env) + headers.each { |key, value| + if key.downcase == 'content-length' + ## There must not be a Content-Length header when the + ## +Status+ is 1xx, 204 or 304. + assert("Content-Length header found in #{status} response, not allowed") { + not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i + } + + bytes = 0 + string_body = true + + if @body.respond_to?(:to_ary) + @body.each { |part| + unless part.kind_of?(String) + string_body = false + break + end + + bytes += Rack::Utils.bytesize(part) + } + + if env["REQUEST_METHOD"] == "HEAD" + assert("Response body was given for HEAD request, but should be empty") { + bytes == 0 + } + else + if string_body + assert("Content-Length header was #{value}, but should be #{bytes}") { + value == bytes.to_s + } + end + end + end + + return + end + } + end + + ## === The Body + def each + @closed = false + ## The Body must respond to +each+ + @body.each { |part| + ## and must only yield String values. + assert("Body yielded non-string value #{part.inspect}") { + part.instance_of? String + } + yield part + } + ## + ## The Body itself should not be an instance of String, as this will + ## break in Ruby 1.9. + ## + ## If the Body responds to +close+, it will be called after iteration. + # XXX howto: assert("Body has not been closed") { @closed } + + + ## + ## If the Body responds to +to_path+, it must return a String + ## identifying the location of a file whose contents are identical + ## to that produced by calling +each+; this may be used by the + ## server as an alternative, possibly more efficient way to + ## transport the response. + + if @body.respond_to?(:to_path) + assert("The file identified by body.to_path does not exist") { + ::File.exist? @body.to_path + } + end + + ## + ## The Body commonly is an Array of Strings, the application + ## instance itself, or a File-like object. + end + + def close + @closed = true + @body.close if @body.respond_to?(:close) + end + + # :startdoc: + + end +end + +## == Thanks +## Some parts of this specification are adopted from PEP333: Python +## Web Server Gateway Interface +## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank +## everyone involved in that effort. diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lobster.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lobster.rb new file mode 100644 index 0000000..f63f419 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lobster.rb @@ -0,0 +1,65 @@ +require 'zlib' + +require 'rack/request' +require 'rack/response' + +module Rack + # Paste has a Pony, Rack has a Lobster! + class Lobster + LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2 + P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0 + t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ + I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0]) + + LambdaLobster = lambda { |env| + if env["QUERY_STRING"].include?("flip") + lobster = LobsterString.split("\n"). + map { |line| line.ljust(42).reverse }. + join("\n") + href = "?" + else + lobster = LobsterString + href = "?flip" + end + + content = ["Lobstericious!", + "
", lobster, "
", + "flip!"] + length = content.inject(0) { |a,e| a+e.size }.to_s + [200, {"Content-Type" => "text/html", "Content-Length" => length}, content] + } + + def call(env) + req = Request.new(env) + if req.GET["flip"] == "left" + lobster = LobsterString.split("\n"). + map { |line| line.ljust(42).reverse }. + join("\n") + href = "?flip=right" + elsif req.GET["flip"] == "crash" + raise "Lobster crashed" + else + lobster = LobsterString + href = "?flip=left" + end + + res = Response.new + res.write "Lobstericious!" + res.write "
"
+      res.write lobster
+      res.write "
" + res.write "

flip!

" + res.write "

crash!

" + res.finish + end + + end +end + +if $0 == __FILE__ + require 'rack' + require 'rack/showexceptions' + Rack::Handler::WEBrick.run \ + Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), + :Port => 9292 +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lock.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lock.rb new file mode 100644 index 0000000..9323852 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lock.rb @@ -0,0 +1,16 @@ +module Rack + class Lock + FLAG = 'rack.multithread'.freeze + + def initialize(app, lock = Mutex.new) + @app, @lock = app, lock + end + + def call(env) + old, env[FLAG] = env[FLAG], false + @lock.synchronize { @app.call(env) } + ensure + env[FLAG] = old + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/methodoverride.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/methodoverride.rb new file mode 100644 index 0000000..0eed29f --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/methodoverride.rb @@ -0,0 +1,27 @@ +module Rack + class MethodOverride + HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS) + + METHOD_OVERRIDE_PARAM_KEY = "_method".freeze + HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze + + def initialize(app) + @app = app + end + + def call(env) + if env["REQUEST_METHOD"] == "POST" + req = Request.new(env) + method = req.POST[METHOD_OVERRIDE_PARAM_KEY] || + env[HTTP_METHOD_OVERRIDE_HEADER] + method = method.to_s.upcase + if HTTP_METHODS.include?(method) + env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"] + env["REQUEST_METHOD"] = method + end + end + + @app.call(env) + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mime.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mime.rb new file mode 100644 index 0000000..5a6a73a --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mime.rb @@ -0,0 +1,204 @@ +module Rack + module Mime + # Returns String with mime type if found, otherwise use +fallback+. + # +ext+ should be filename extension in the '.ext' format that + # File.extname(file) returns. + # +fallback+ may be any object + # + # Also see the documentation for MIME_TYPES + # + # Usage: + # Rack::Mime.mime_type('.foo') + # + # This is a shortcut for: + # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream') + + def mime_type(ext, fallback='application/octet-stream') + MIME_TYPES.fetch(ext, fallback) + end + module_function :mime_type + + # List of most common mime-types, selected various sources + # according to their usefulness in a webserving scope for Ruby + # users. + # + # To amend this list with your local mime.types list you can use: + # + # require 'webrick/httputils' + # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types') + # Rack::Mime::MIME_TYPES.merge!(list) + # + # To add the list mongrel provides, use: + # + # require 'mongrel/handlers' + # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES) + + MIME_TYPES = { + ".3gp" => "video/3gpp", + ".a" => "application/octet-stream", + ".ai" => "application/postscript", + ".aif" => "audio/x-aiff", + ".aiff" => "audio/x-aiff", + ".asc" => "application/pgp-signature", + ".asf" => "video/x-ms-asf", + ".asm" => "text/x-asm", + ".asx" => "video/x-ms-asf", + ".atom" => "application/atom+xml", + ".au" => "audio/basic", + ".avi" => "video/x-msvideo", + ".bat" => "application/x-msdownload", + ".bin" => "application/octet-stream", + ".bmp" => "image/bmp", + ".bz2" => "application/x-bzip2", + ".c" => "text/x-c", + ".cab" => "application/vnd.ms-cab-compressed", + ".cc" => "text/x-c", + ".chm" => "application/vnd.ms-htmlhelp", + ".class" => "application/octet-stream", + ".com" => "application/x-msdownload", + ".conf" => "text/plain", + ".cpp" => "text/x-c", + ".crt" => "application/x-x509-ca-cert", + ".css" => "text/css", + ".csv" => "text/csv", + ".cxx" => "text/x-c", + ".deb" => "application/x-debian-package", + ".der" => "application/x-x509-ca-cert", + ".diff" => "text/x-diff", + ".djv" => "image/vnd.djvu", + ".djvu" => "image/vnd.djvu", + ".dll" => "application/x-msdownload", + ".dmg" => "application/octet-stream", + ".doc" => "application/msword", + ".dot" => "application/msword", + ".dtd" => "application/xml-dtd", + ".dvi" => "application/x-dvi", + ".ear" => "application/java-archive", + ".eml" => "message/rfc822", + ".eps" => "application/postscript", + ".exe" => "application/x-msdownload", + ".f" => "text/x-fortran", + ".f77" => "text/x-fortran", + ".f90" => "text/x-fortran", + ".flv" => "video/x-flv", + ".for" => "text/x-fortran", + ".gem" => "application/octet-stream", + ".gemspec" => "text/x-script.ruby", + ".gif" => "image/gif", + ".gz" => "application/x-gzip", + ".h" => "text/x-c", + ".hh" => "text/x-c", + ".htm" => "text/html", + ".html" => "text/html", + ".ico" => "image/vnd.microsoft.icon", + ".ics" => "text/calendar", + ".ifb" => "text/calendar", + ".iso" => "application/octet-stream", + ".jar" => "application/java-archive", + ".java" => "text/x-java-source", + ".jnlp" => "application/x-java-jnlp-file", + ".jpeg" => "image/jpeg", + ".jpg" => "image/jpeg", + ".js" => "application/javascript", + ".json" => "application/json", + ".log" => "text/plain", + ".m3u" => "audio/x-mpegurl", + ".m4v" => "video/mp4", + ".man" => "text/troff", + ".mathml" => "application/mathml+xml", + ".mbox" => "application/mbox", + ".mdoc" => "text/troff", + ".me" => "text/troff", + ".mid" => "audio/midi", + ".midi" => "audio/midi", + ".mime" => "message/rfc822", + ".mml" => "application/mathml+xml", + ".mng" => "video/x-mng", + ".mov" => "video/quicktime", + ".mp3" => "audio/mpeg", + ".mp4" => "video/mp4", + ".mp4v" => "video/mp4", + ".mpeg" => "video/mpeg", + ".mpg" => "video/mpeg", + ".ms" => "text/troff", + ".msi" => "application/x-msdownload", + ".odp" => "application/vnd.oasis.opendocument.presentation", + ".ods" => "application/vnd.oasis.opendocument.spreadsheet", + ".odt" => "application/vnd.oasis.opendocument.text", + ".ogg" => "application/ogg", + ".p" => "text/x-pascal", + ".pas" => "text/x-pascal", + ".pbm" => "image/x-portable-bitmap", + ".pdf" => "application/pdf", + ".pem" => "application/x-x509-ca-cert", + ".pgm" => "image/x-portable-graymap", + ".pgp" => "application/pgp-encrypted", + ".pkg" => "application/octet-stream", + ".pl" => "text/x-script.perl", + ".pm" => "text/x-script.perl-module", + ".png" => "image/png", + ".pnm" => "image/x-portable-anymap", + ".ppm" => "image/x-portable-pixmap", + ".pps" => "application/vnd.ms-powerpoint", + ".ppt" => "application/vnd.ms-powerpoint", + ".ps" => "application/postscript", + ".psd" => "image/vnd.adobe.photoshop", + ".py" => "text/x-script.python", + ".qt" => "video/quicktime", + ".ra" => "audio/x-pn-realaudio", + ".rake" => "text/x-script.ruby", + ".ram" => "audio/x-pn-realaudio", + ".rar" => "application/x-rar-compressed", + ".rb" => "text/x-script.ruby", + ".rdf" => "application/rdf+xml", + ".roff" => "text/troff", + ".rpm" => "application/x-redhat-package-manager", + ".rss" => "application/rss+xml", + ".rtf" => "application/rtf", + ".ru" => "text/x-script.ruby", + ".s" => "text/x-asm", + ".sgm" => "text/sgml", + ".sgml" => "text/sgml", + ".sh" => "application/x-sh", + ".sig" => "application/pgp-signature", + ".snd" => "audio/basic", + ".so" => "application/octet-stream", + ".svg" => "image/svg+xml", + ".svgz" => "image/svg+xml", + ".swf" => "application/x-shockwave-flash", + ".t" => "text/troff", + ".tar" => "application/x-tar", + ".tbz" => "application/x-bzip-compressed-tar", + ".tcl" => "application/x-tcl", + ".tex" => "application/x-tex", + ".texi" => "application/x-texinfo", + ".texinfo" => "application/x-texinfo", + ".text" => "text/plain", + ".tif" => "image/tiff", + ".tiff" => "image/tiff", + ".torrent" => "application/x-bittorrent", + ".tr" => "text/troff", + ".txt" => "text/plain", + ".vcf" => "text/x-vcard", + ".vcs" => "text/x-vcalendar", + ".vrml" => "model/vrml", + ".war" => "application/java-archive", + ".wav" => "audio/x-wav", + ".wma" => "audio/x-ms-wma", + ".wmv" => "video/x-ms-wmv", + ".wmx" => "video/x-ms-wmx", + ".wrl" => "model/vrml", + ".wsdl" => "application/wsdl+xml", + ".xbm" => "image/x-xbitmap", + ".xhtml" => "application/xhtml+xml", + ".xls" => "application/vnd.ms-excel", + ".xml" => "application/xml", + ".xpm" => "image/x-xpixmap", + ".xsl" => "application/xml", + ".xslt" => "application/xslt+xml", + ".yaml" => "text/yaml", + ".yml" => "text/yaml", + ".zip" => "application/zip", + } + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mock.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mock.rb new file mode 100644 index 0000000..fdefb03 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mock.rb @@ -0,0 +1,184 @@ +require 'uri' +require 'stringio' +require 'rack/lint' +require 'rack/utils' +require 'rack/response' + +module Rack + # Rack::MockRequest helps testing your Rack application without + # actually using HTTP. + # + # After performing a request on a URL with get/post/put/delete, it + # returns a MockResponse with useful helper methods for effective + # testing. + # + # You can pass a hash with additional configuration to the + # get/post/put/delete. + # :input:: A String or IO-like to be used as rack.input. + # :fatal:: Raise a FatalWarning if the app writes to rack.errors. + # :lint:: If true, wrap the application in a Rack::Lint. + + class MockRequest + class FatalWarning < RuntimeError + end + + class FatalWarner + def puts(warning) + raise FatalWarning, warning + end + + def write(warning) + raise FatalWarning, warning + end + + def flush + end + + def string + "" + end + end + + DEFAULT_ENV = { + "rack.version" => [1,0], + "rack.input" => StringIO.new, + "rack.errors" => StringIO.new, + "rack.multithread" => true, + "rack.multiprocess" => true, + "rack.run_once" => false, + } + + def initialize(app) + @app = app + end + + def get(uri, opts={}) request("GET", uri, opts) end + def post(uri, opts={}) request("POST", uri, opts) end + def put(uri, opts={}) request("PUT", uri, opts) end + def delete(uri, opts={}) request("DELETE", uri, opts) end + + def request(method="GET", uri="", opts={}) + env = self.class.env_for(uri, opts.merge(:method => method)) + + if opts[:lint] + app = Rack::Lint.new(@app) + else + app = @app + end + + errors = env["rack.errors"] + MockResponse.new(*(app.call(env) + [errors])) + end + + # Return the Rack environment used for a request to +uri+. + def self.env_for(uri="", opts={}) + uri = URI(uri) + uri.path = "/#{uri.path}" unless uri.path[0] == ?/ + + env = DEFAULT_ENV.dup + + env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET" + env["SERVER_NAME"] = uri.host || "example.org" + env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80" + env["QUERY_STRING"] = uri.query.to_s + env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path + env["rack.url_scheme"] = uri.scheme || "http" + env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off" + + env["SCRIPT_NAME"] = opts[:script_name] || "" + + if opts[:fatal] + env["rack.errors"] = FatalWarner.new + else + env["rack.errors"] = StringIO.new + end + + if params = opts[:params] + if env["REQUEST_METHOD"] == "GET" + params = Utils.parse_nested_query(params) if params.is_a?(String) + params.update(Utils.parse_nested_query(env["QUERY_STRING"])) + env["QUERY_STRING"] = Utils.build_nested_query(params) + elsif !opts.has_key?(:input) + opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded" + if params.is_a?(Hash) + if data = Utils::Multipart.build_multipart(params) + opts[:input] = data + opts["CONTENT_LENGTH"] ||= data.length.to_s + opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}" + else + opts[:input] = Utils.build_nested_query(params) + end + else + opts[:input] = params + end + end + end + + opts[:input] ||= "" + if String === opts[:input] + env["rack.input"] = StringIO.new(opts[:input]) + else + env["rack.input"] = opts[:input] + end + + env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s + + opts.each { |field, value| + env[field] = value if String === field + } + + env + end + end + + # Rack::MockResponse provides useful helpers for testing your apps. + # Usually, you don't create the MockResponse on your own, but use + # MockRequest. + + class MockResponse + def initialize(status, headers, body, errors=StringIO.new("")) + @status = status.to_i + + @original_headers = headers + @headers = Rack::Utils::HeaderHash.new + headers.each { |field, values| + @headers[field] = values + @headers[field] = "" if values.empty? + } + + @body = "" + body.each { |part| @body << part } + + @errors = errors.string if errors.respond_to?(:string) + end + + # Status + attr_reader :status + + # Headers + attr_reader :headers, :original_headers + + def [](field) + headers[field] + end + + + # Body + attr_reader :body + + def =~(other) + @body =~ other + end + + def match(other) + @body.match other + end + + + # Errors + attr_accessor :errors + + + include Response::Helpers + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/recursive.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/recursive.rb new file mode 100644 index 0000000..bf8b965 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/recursive.rb @@ -0,0 +1,57 @@ +require 'uri' + +module Rack + # Rack::ForwardRequest gets caught by Rack::Recursive and redirects + # the current request to the app at +url+. + # + # raise ForwardRequest.new("/not-found") + # + + class ForwardRequest < Exception + attr_reader :url, :env + + def initialize(url, env={}) + @url = URI(url) + @env = env + + @env["PATH_INFO"] = @url.path + @env["QUERY_STRING"] = @url.query if @url.query + @env["HTTP_HOST"] = @url.host if @url.host + @env["HTTP_PORT"] = @url.port if @url.port + @env["rack.url_scheme"] = @url.scheme if @url.scheme + + super "forwarding to #{url}" + end + end + + # Rack::Recursive allows applications called down the chain to + # include data from other applications (by using + # rack['rack.recursive.include'][...] or raise a + # ForwardRequest to redirect internally. + + class Recursive + def initialize(app) + @app = app + end + + def call(env) + @script_name = env["SCRIPT_NAME"] + @app.call(env.merge('rack.recursive.include' => method(:include))) + rescue ForwardRequest => req + call(env.merge(req.env)) + end + + def include(env, path) + unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ || + path[@script_name.size].nil?) + raise ArgumentError, "can only include below #{@script_name}, not #{path}" + end + + env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name, + "REQUEST_METHOD" => "GET", + "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "", + "rack.input" => StringIO.new("")) + @app.call(env) + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/reloader.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/reloader.rb new file mode 100644 index 0000000..aa2f060 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/reloader.rb @@ -0,0 +1,106 @@ +# Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com +# All files in this distribution are subject to the terms of the Ruby license. + +require 'pathname' + +module Rack + + # High performant source reloader + # + # This class acts as Rack middleware. + # + # What makes it especially suited for use in a production environment is that + # any file will only be checked once and there will only be made one system + # call stat(2). + # + # Please note that this will not reload files in the background, it does so + # only when actively called. + # + # It is performing a check/reload cycle at the start of every request, but + # also respects a cool down time, during which nothing will be done. + class Reloader + def initialize(app, cooldown = 10, backend = Stat) + @app = app + @cooldown = cooldown + @last = (Time.now - cooldown) + @cache = {} + @mtimes = {} + + extend backend + end + + def call(env) + if @cooldown and Time.now > @last + @cooldown + if Thread.list.size > 1 + Thread.exclusive{ reload! } + else + reload! + end + + @last = Time.now + end + + @app.call(env) + end + + def reload!(stderr = $stderr) + rotation do |file, mtime| + previous_mtime = @mtimes[file] ||= mtime + safe_load(file, mtime, stderr) if mtime > previous_mtime + end + end + + # A safe Kernel::load, issuing the hooks depending on the results + def safe_load(file, mtime, stderr = $stderr) + load(file) + stderr.puts "#{self.class}: reloaded `#{file}'" + file + rescue LoadError, SyntaxError => ex + stderr.puts ex + ensure + @mtimes[file] = mtime + end + + module Stat + def rotation + files = [$0, *$LOADED_FEATURES].uniq + paths = ['./', *$LOAD_PATH].uniq + + files.map{|file| + next if file =~ /\.(so|bundle)$/ # cannot reload compiled files + + found, stat = figure_path(file, paths) + next unless found and stat and mtime = stat.mtime + + @cache[file] = found + + yield(found, mtime) + }.compact + end + + # Takes a relative or absolute +file+ name, a couple possible +paths+ that + # the +file+ might reside in. Returns the full path and File::Stat for the + # path. + def figure_path(file, paths) + found = @cache[file] + found = file if !found and Pathname.new(file).absolute? + found, stat = safe_stat(found) + return found, stat if found + + paths.each do |possible_path| + path = ::File.join(possible_path, file) + found, stat = safe_stat(path) + return ::File.expand_path(found), stat if found + end + end + + def safe_stat(file) + return unless file + stat = ::File.stat(file) + return file, stat if stat.file? + rescue Errno::ENOENT, Errno::ENOTDIR + @cache.delete(file) and false + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/request.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/request.rb new file mode 100644 index 0000000..04cdde0 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/request.rb @@ -0,0 +1,254 @@ +require 'rack/utils' + +module Rack + # Rack::Request provides a convenient interface to a Rack + # environment. It is stateless, the environment +env+ passed to the + # constructor will be directly modified. + # + # req = Rack::Request.new(env) + # req.post? + # req.params["data"] + # + # The environment hash passed will store a reference to the Request object + # instantiated so that it will only instantiate if an instance of the Request + # object doesn't already exist. + + class Request + # The environment of the request. + attr_reader :env + + def self.new(env, *args) + if self == Rack::Request + env["rack.request"] ||= super + else + super + end + end + + def initialize(env) + @env = env + end + + def body; @env["rack.input"] end + def scheme; @env["rack.url_scheme"] end + def script_name; @env["SCRIPT_NAME"].to_s end + def path_info; @env["PATH_INFO"].to_s end + def port; @env["SERVER_PORT"].to_i end + def request_method; @env["REQUEST_METHOD"] end + def query_string; @env["QUERY_STRING"].to_s end + def content_length; @env['CONTENT_LENGTH'] end + def content_type; @env['CONTENT_TYPE'] end + def session; @env['rack.session'] ||= {} end + def session_options; @env['rack.session.options'] ||= {} end + + # The media type (type/subtype) portion of the CONTENT_TYPE header + # without any media type parameters. e.g., when CONTENT_TYPE is + # "text/plain;charset=utf-8", the media-type is "text/plain". + # + # For more information on the use of media types in HTTP, see: + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 + def media_type + content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase + end + + # The media type parameters provided in CONTENT_TYPE as a Hash, or + # an empty Hash if no CONTENT_TYPE or media-type parameters were + # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8", + # this method responds with the following Hash: + # { 'charset' => 'utf-8' } + def media_type_params + return {} if content_type.nil? + content_type.split(/\s*[;,]\s*/)[1..-1]. + collect { |s| s.split('=', 2) }. + inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash } + end + + # The character set of the request body if a "charset" media type + # parameter was given, or nil if no "charset" was specified. Note + # that, per RFC2616, text/* media types that specify no explicit + # charset are to be considered ISO-8859-1. + def content_charset + media_type_params['charset'] + end + + def host + # Remove port number. + (@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '') + end + + def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end + def path_info=(s); @env["PATH_INFO"] = s.to_s end + + def get?; request_method == "GET" end + def post?; request_method == "POST" end + def put?; request_method == "PUT" end + def delete?; request_method == "DELETE" end + def head?; request_method == "HEAD" end + + # The set of form-data media-types. Requests that do not indicate + # one of the media types presents in this list will not be eligible + # for form-data / param parsing. + FORM_DATA_MEDIA_TYPES = [ + nil, + 'application/x-www-form-urlencoded', + 'multipart/form-data' + ] + + # The set of media-types. Requests that do not indicate + # one of the media types presents in this list will not be eligible + # for param parsing like soap attachments or generic multiparts + PARSEABLE_DATA_MEDIA_TYPES = [ + 'multipart/related', + 'multipart/mixed' + ] + + # Determine whether the request body contains form-data by checking + # the request media_type against registered form-data media-types: + # "application/x-www-form-urlencoded" and "multipart/form-data". The + # list of form-data media types can be modified through the + # +FORM_DATA_MEDIA_TYPES+ array. + def form_data? + FORM_DATA_MEDIA_TYPES.include?(media_type) + end + + # Determine whether the request body contains data by checking + # the request media_type against registered parse-data media-types + def parseable_data? + PARSEABLE_DATA_MEDIA_TYPES.include?(media_type) + end + + # Returns the data recieved in the query string. + def GET + if @env["rack.request.query_string"] == query_string + @env["rack.request.query_hash"] + else + @env["rack.request.query_string"] = query_string + @env["rack.request.query_hash"] = + Utils.parse_nested_query(query_string) + end + end + + # Returns the data recieved in the request body. + # + # This method support both application/x-www-form-urlencoded and + # multipart/form-data. + def POST + if @env["rack.request.form_input"].eql? @env["rack.input"] + @env["rack.request.form_hash"] + elsif form_data? || parseable_data? + @env["rack.request.form_input"] = @env["rack.input"] + unless @env["rack.request.form_hash"] = + Utils::Multipart.parse_multipart(env) + form_vars = @env["rack.input"].read + + # Fix for Safari Ajax postings that always append \0 + form_vars.sub!(/\0\z/, '') + + @env["rack.request.form_vars"] = form_vars + @env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars) + + @env["rack.input"].rewind + end + @env["rack.request.form_hash"] + else + {} + end + end + + # The union of GET and POST data. + def params + self.put? ? self.GET : self.GET.update(self.POST) + rescue EOFError => e + self.GET + end + + # shortcut for request.params[key] + def [](key) + params[key.to_s] + end + + # shortcut for request.params[key] = value + def []=(key, value) + params[key.to_s] = value + end + + # like Hash#values_at + def values_at(*keys) + keys.map{|key| params[key] } + end + + # the referer of the client or '/' + def referer + @env['HTTP_REFERER'] || '/' + end + alias referrer referer + + + def cookies + return {} unless @env["HTTP_COOKIE"] + + if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] + @env["rack.request.cookie_hash"] + else + @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] + # According to RFC 2109: + # If multiple cookies satisfy the criteria above, they are ordered in + # the Cookie header such that those with more specific Path attributes + # precede those with less specific. Ordering with respect to other + # attributes (e.g., Domain) is unspecified. + @env["rack.request.cookie_hash"] = + Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)| + h[k] = Array === v ? v.first : v + h + } + end + end + + def xhr? + @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" + end + + # Tries to return a remake of the original request URL as a string. + def url + url = scheme + "://" + url << host + + if scheme == "https" && port != 443 || + scheme == "http" && port != 80 + url << ":#{port}" + end + + url << fullpath + + url + end + + def path + script_name + path_info + end + + def fullpath + query_string.empty? ? path : "#{path}?#{query_string}" + end + + def accept_encoding + @env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part| + m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick + + if m + [m[1], (m[2] || 1.0).to_f] + else + raise "Invalid value for Accept-Encoding: #{part.inspect}" + end + end + end + + def ip + if addr = @env['HTTP_X_FORWARDED_FOR'] + addr.split(',').last.strip + else + @env['REMOTE_ADDR'] + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/response.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/response.rb new file mode 100644 index 0000000..28b4d83 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/response.rb @@ -0,0 +1,183 @@ +require 'rack/request' +require 'rack/utils' + +module Rack + # Rack::Response provides a convenient interface to create a Rack + # response. + # + # It allows setting of headers and cookies, and provides useful + # defaults (a OK response containing HTML). + # + # You can use Response#write to iteratively generate your response, + # but note that this is buffered by Rack::Response until you call + # +finish+. +finish+ however can take a block inside which calls to + # +write+ are syncronous with the Rack response. + # + # Your application's +call+ should end returning Response#finish. + + class Response + attr_accessor :length + + def initialize(body=[], status=200, header={}, &block) + @status = status + @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}. + merge(header)) + + @writer = lambda { |x| @body << x } + @block = nil + @length = 0 + + @body = [] + + if body.respond_to? :to_str + write body.to_str + elsif body.respond_to?(:each) + body.each { |part| + write part.to_s + } + else + raise TypeError, "stringable or iterable required" + end + + yield self if block_given? + end + + attr_reader :header + attr_accessor :status, :body + + def [](key) + header[key] + end + + def []=(key, value) + header[key] = value + end + + def set_cookie(key, value) + case value + when Hash + domain = "; domain=" + value[:domain] if value[:domain] + path = "; path=" + value[:path] if value[:path] + # According to RFC 2109, we need dashes here. + # N.B.: cgi.rb uses spaces... + expires = "; expires=" + value[:expires].clone.gmtime. + strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires] + secure = "; secure" if value[:secure] + httponly = "; HttpOnly" if value[:httponly] + value = value[:value] + end + value = [value] unless Array === value + cookie = Utils.escape(key) + "=" + + value.map { |v| Utils.escape v }.join("&") + + "#{domain}#{path}#{expires}#{secure}#{httponly}" + + case self["Set-Cookie"] + when Array + self["Set-Cookie"] << cookie + when String + self["Set-Cookie"] = [self["Set-Cookie"], cookie] + when nil + self["Set-Cookie"] = cookie + end + end + + def delete_cookie(key, value={}) + unless Array === self["Set-Cookie"] + self["Set-Cookie"] = [self["Set-Cookie"]].compact + end + + self["Set-Cookie"].reject! { |cookie| + cookie =~ /\A#{Utils.escape(key)}=/ + } + + set_cookie(key, + {:value => '', :path => nil, :domain => nil, + :expires => Time.at(0) }.merge(value)) + end + + def redirect(target, status=302) + self.status = status + self["Location"] = target + end + + def finish(&block) + @block = block + + if [204, 304].include?(status.to_i) + header.delete "Content-Type" + [status.to_i, header.to_hash, []] + else + [status.to_i, header.to_hash, self] + end + end + alias to_a finish # For *response + + def each(&callback) + @body.each(&callback) + @writer = callback + @block.call(self) if @block + end + + # Append to body and update Content-Length. + # + # NOTE: Do not mix #write and direct #body access! + # + def write(str) + s = str.to_s + @length += Rack::Utils.bytesize(s) + @writer.call s + + header["Content-Length"] = @length.to_s + str + end + + def close + body.close if body.respond_to?(:close) + end + + def empty? + @block == nil && @body.empty? + end + + alias headers header + + module Helpers + def invalid?; @status < 100 || @status >= 600; end + + def informational?; @status >= 100 && @status < 200; end + def successful?; @status >= 200 && @status < 300; end + def redirection?; @status >= 300 && @status < 400; end + def client_error?; @status >= 400 && @status < 500; end + def server_error?; @status >= 500 && @status < 600; end + + def ok?; @status == 200; end + def forbidden?; @status == 403; end + def not_found?; @status == 404; end + + def redirect?; [301, 302, 303, 307].include? @status; end + def empty?; [201, 204, 304].include? @status; end + + # Headers + attr_reader :headers, :original_headers + + def include?(header) + !!headers[header] + end + + def content_type + headers["Content-Type"] + end + + def content_length + cl = headers["Content-Length"] + cl ? cl.to_i : cl + end + + def location + headers["Location"] + end + end + + include Helpers + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/rewindable_input.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/rewindable_input.rb new file mode 100644 index 0000000..2347dc3 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/rewindable_input.rb @@ -0,0 +1,98 @@ +require 'tempfile' + +module Rack + # Class which can make any IO object rewindable, including non-rewindable ones. It does + # this by buffering the data into a tempfile, which is rewindable. + # + # rack.input is required to be rewindable, so if your input stream IO is non-rewindable + # by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class + # to easily make it rewindable. + # + # Don't forget to call #close when you're done. This frees up temporary resources that + # RewindableInput uses, though it does *not* close the original IO object. + class RewindableInput + def initialize(io) + @io = io + @rewindable_io = nil + @unlinked = false + end + + def gets + make_rewindable unless @rewindable_io + @rewindable_io.gets + end + + def read(*args) + make_rewindable unless @rewindable_io + @rewindable_io.read(*args) + end + + def each(&block) + make_rewindable unless @rewindable_io + @rewindable_io.each(&block) + end + + def rewind + make_rewindable unless @rewindable_io + @rewindable_io.rewind + end + + # Closes this RewindableInput object without closing the originally + # wrapped IO oject. Cleans up any temporary resources that this RewindableInput + # has created. + # + # This method may be called multiple times. It does nothing on subsequent calls. + def close + if @rewindable_io + if @unlinked + @rewindable_io.close + else + @rewindable_io.close! + end + @rewindable_io = nil + end + end + + private + + # Ruby's Tempfile class has a bug. Subclass it and fix it. + class Tempfile < ::Tempfile + def _close + @tmpfile.close if @tmpfile + @data[1] = nil if @data + @tmpfile = nil + end + end + + def make_rewindable + # Buffer all data into a tempfile. Since this tempfile is private to this + # RewindableInput object, we chmod it so that nobody else can read or write + # it. On POSIX filesystems we also unlink the file so that it doesn't + # even have a file entry on the filesystem anymore, though we can still + # access it because we have the file handle open. + @rewindable_io = Tempfile.new('RackRewindableInput') + @rewindable_io.chmod(0000) + if filesystem_has_posix_semantics? + @rewindable_io.unlink + @unlinked = true + end + + buffer = "" + while @io.read(1024 * 4, buffer) + entire_buffer_written_out = false + while !entire_buffer_written_out + written = @rewindable_io.write(buffer) + entire_buffer_written_out = written == buffer.size + if !entire_buffer_written_out + buffer.slice!(0 .. written - 1) + end + end + end + @rewindable_io.rewind + end + + def filesystem_has_posix_semantics? + RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/ + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/abstract/id.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/abstract/id.rb new file mode 100644 index 0000000..218144c --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/abstract/id.rb @@ -0,0 +1,142 @@ +# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net +# bugrep: Andreas Zehnder + +require 'time' +require 'rack/request' +require 'rack/response' + +module Rack + + module Session + + module Abstract + + # ID sets up a basic framework for implementing an id based sessioning + # service. Cookies sent to the client for maintaining sessions will only + # contain an id reference. Only #get_session and #set_session are + # required to be overwritten. + # + # All parameters are optional. + # * :key determines the name of the cookie, by default it is + # 'rack.session' + # * :path, :domain, :expire_after, :secure, and :httponly set the related + # cookie options as by Rack::Response#add_cookie + # * :defer will not set a cookie in the response. + # * :renew (implementation dependent) will prompt the generation of a new + # session id, and migration of data to be referenced at the new id. If + # :defer is set, it will be overridden and the cookie will be set. + # * :sidbits sets the number of bits in length that a generated session + # id will be. + # + # These options can be set on a per request basis, at the location of + # env['rack.session.options']. Additionally the id of the session can be + # found within the options hash at the key :id. It is highly not + # recommended to change its value. + # + # Is Rack::Utils::Context compatible. + + class ID + DEFAULT_OPTIONS = { + :path => '/', + :domain => nil, + :expire_after => nil, + :secure => false, + :httponly => true, + :defer => false, + :renew => false, + :sidbits => 128 + } + + attr_reader :key, :default_options + def initialize(app, options={}) + @app = app + @key = options[:key] || "rack.session" + @default_options = self.class::DEFAULT_OPTIONS.merge(options) + end + + def call(env) + context(env) + end + + def context(env, app=@app) + load_session(env) + status, headers, body = app.call(env) + commit_session(env, status, headers, body) + end + + private + + # Generate a new session id using Ruby #rand. The size of the + # session id is controlled by the :sidbits option. + # Monkey patch this to use custom methods for session id generation. + + def generate_sid + "%0#{@default_options[:sidbits] / 4}x" % + rand(2**@default_options[:sidbits] - 1) + end + + # Extracts the session id from provided cookies and passes it and the + # environment to #get_session. It then sets the resulting session into + # 'rack.session', and places options and session metadata into + # 'rack.session.options'. + + def load_session(env) + request = Rack::Request.new(env) + session_id = request.cookies[@key] + + begin + session_id, session = get_session(env, session_id) + env['rack.session'] = session + rescue + env['rack.session'] = Hash.new + end + + env['rack.session.options'] = @default_options. + merge(:id => session_id) + end + + # Acquires the session from the environment and the session id from + # the session options and passes them to #set_session. If successful + # and the :defer option is not true, a cookie will be added to the + # response with the session's id. + + def commit_session(env, status, headers, body) + session = env['rack.session'] + options = env['rack.session.options'] + session_id = options[:id] + + if not session_id = set_session(env, session_id, session, options) + env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.") + [status, headers, body] + elsif options[:defer] and not options[:renew] + env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE + [status, headers, body] + else + cookie = Hash.new + cookie[:value] = session_id + cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? + response = Rack::Response.new(body, status, headers) + response.set_cookie(@key, cookie.merge(options)) + response.to_a + end + end + + # All thread safety and session retrival proceedures should occur here. + # Should return [session_id, session]. + # If nil is provided as the session id, generation of a new valid id + # should occur within. + + def get_session(env, sid) + raise '#get_session not implemented.' + end + + # All thread safety and session storage proceedures should occur here. + # Should return true or false dependant on whether or not the session + # was saved or not. + def set_session(env, sid, session, options) + raise '#set_session not implemented.' + end + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/cookie.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/cookie.rb new file mode 100644 index 0000000..eace9bd --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/cookie.rb @@ -0,0 +1,91 @@ +require 'openssl' +require 'rack/request' +require 'rack/response' + +module Rack + + module Session + + # Rack::Session::Cookie provides simple cookie based session management. + # The session is a Ruby Hash stored as base64 encoded marshalled data + # set to :key (default: rack.session). + # When the secret key is set, cookie data is checked for data integrity. + # + # Example: + # + # use Rack::Session::Cookie, :key => 'rack.session', + # :domain => 'foo.com', + # :path => '/', + # :expire_after => 2592000, + # :secret => 'change_me' + # + # All parameters are optional. + + class Cookie + + def initialize(app, options={}) + @app = app + @key = options[:key] || "rack.session" + @secret = options[:secret] + @default_options = {:domain => nil, + :path => "/", + :expire_after => nil}.merge(options) + end + + def call(env) + load_session(env) + status, headers, body = @app.call(env) + commit_session(env, status, headers, body) + end + + private + + def load_session(env) + request = Rack::Request.new(env) + session_data = request.cookies[@key] + + if @secret && session_data + session_data, digest = session_data.split("--") + session_data = nil unless digest == generate_hmac(session_data) + end + + begin + session_data = session_data.unpack("m*").first + session_data = Marshal.load(session_data) + env["rack.session"] = session_data + rescue + env["rack.session"] = Hash.new + end + + env["rack.session.options"] = @default_options.dup + end + + def commit_session(env, status, headers, body) + session_data = Marshal.dump(env["rack.session"]) + session_data = [session_data].pack("m*") + + if @secret + session_data = "#{session_data}--#{generate_hmac(session_data)}" + end + + if session_data.size > (4096 - @key.size) + env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.") + [status, headers, body] + else + options = env["rack.session.options"] + cookie = Hash.new + cookie[:value] = session_data + cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? + response = Rack::Response.new(body, status, headers) + response.set_cookie(@key, cookie.merge(options)) + response.to_a + end + end + + def generate_hmac(data) + OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data) + end + + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/memcache.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/memcache.rb new file mode 100644 index 0000000..4a65cbf --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/memcache.rb @@ -0,0 +1,109 @@ +# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net + +require 'rack/session/abstract/id' +require 'memcache' + +module Rack + module Session + # Rack::Session::Memcache provides simple cookie based session management. + # Session data is stored in memcached. The corresponding session key is + # maintained in the cookie. + # You may treat Session::Memcache as you would Session::Pool with the + # following caveats. + # + # * Setting :expire_after to 0 would note to the Memcache server to hang + # onto the session data until it would drop it according to it's own + # specifications. However, the cookie sent to the client would expire + # immediately. + # + # Note that memcache does drop data before it may be listed to expire. For + # a full description of behaviour, please see memcache's documentation. + + class Memcache < Abstract::ID + attr_reader :mutex, :pool + DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \ + :namespace => 'rack:session', + :memcache_server => 'localhost:11211' + + def initialize(app, options={}) + super + + @mutex = Mutex.new + @pool = MemCache. + new @default_options[:memcache_server], @default_options + raise 'No memcache servers' unless @pool.servers.any?{|s|s.alive?} + end + + def generate_sid + loop do + sid = super + break sid unless @pool.get(sid, true) + end + end + + def get_session(env, sid) + session = @pool.get(sid) if sid + @mutex.lock if env['rack.multithread'] + unless sid and session + env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? + session = {} + sid = generate_sid + ret = @pool.add sid, session + raise "Session collision on '#{sid.inspect}'" unless /^STORED/ =~ ret + end + session.instance_variable_set('@old', {}.merge(session)) + return [sid, session] + rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted + warn "#{self} is unable to find server." + warn $!.inspect + return [ nil, {} ] + ensure + @mutex.unlock if env['rack.multithread'] + end + + def set_session(env, session_id, new_session, options) + expiry = options[:expire_after] + expiry = expiry.nil? ? 0 : expiry + 1 + + @mutex.lock if env['rack.multithread'] + session = @pool.get(session_id) || {} + if options[:renew] or options[:drop] + @pool.delete session_id + return false if options[:drop] + session_id = generate_sid + @pool.add session_id, 0 # so we don't worry about cache miss on #set + end + old_session = new_session.instance_variable_get('@old') || {} + session = merge_sessions session_id, old_session, new_session, session + @pool.set session_id, session, expiry + return session_id + rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted + warn "#{self} is unable to find server." + warn $!.inspect + return false + ensure + @mutex.unlock if env['rack.multithread'] + end + + private + + def merge_sessions sid, old, new, cur=nil + cur ||= {} + unless Hash === old and Hash === new + warn 'Bad old or new sessions provided.' + return cur + end + + delete = old.keys - new.keys + warn "//@#{sid}: delete #{delete*','}" if $VERBOSE and not delete.empty? + delete.each{|k| cur.delete k } + + update = new.keys.select{|k| new[k] != old[k] } + warn "//@#{sid}: update #{update*','}" if $VERBOSE and not update.empty? + update.each{|k| cur[k] = new[k] } + + cur + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/pool.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/pool.rb new file mode 100644 index 0000000..f6f8740 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/pool.rb @@ -0,0 +1,100 @@ +# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net +# THANKS: +# apeiros, for session id generation, expiry setup, and threadiness +# sergio, threadiness and bugreps + +require 'rack/session/abstract/id' +require 'thread' + +module Rack + module Session + # Rack::Session::Pool provides simple cookie based session management. + # Session data is stored in a hash held by @pool. + # In the context of a multithreaded environment, sessions being + # committed to the pool is done in a merging manner. + # + # The :drop option is available in rack.session.options if you with to + # explicitly remove the session from the session cache. + # + # Example: + # myapp = MyRackApp.new + # sessioned = Rack::Session::Pool.new(myapp, + # :domain => 'foo.com', + # :expire_after => 2592000 + # ) + # Rack::Handler::WEBrick.run sessioned + + class Pool < Abstract::ID + attr_reader :mutex, :pool + DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false + + def initialize(app, options={}) + super + @pool = Hash.new + @mutex = Mutex.new + end + + def generate_sid + loop do + sid = super + break sid unless @pool.key? sid + end + end + + def get_session(env, sid) + session = @pool[sid] if sid + @mutex.lock if env['rack.multithread'] + unless sid and session + env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? + session = {} + sid = generate_sid + @pool.store sid, session + end + session.instance_variable_set('@old', {}.merge(session)) + return [sid, session] + ensure + @mutex.unlock if env['rack.multithread'] + end + + def set_session(env, session_id, new_session, options) + @mutex.lock if env['rack.multithread'] + session = @pool[session_id] + if options[:renew] or options[:drop] + @pool.delete session_id + return false if options[:drop] + session_id = generate_sid + @pool.store session_id, 0 + end + old_session = new_session.instance_variable_get('@old') || {} + session = merge_sessions session_id, old_session, new_session, session + @pool.store session_id, session + return session_id + rescue + warn "#{new_session.inspect} has been lost." + warn $!.inspect + ensure + @mutex.unlock if env['rack.multithread'] + end + + private + + def merge_sessions sid, old, new, cur=nil + cur ||= {} + unless Hash === old and Hash === new + warn 'Bad old or new sessions provided.' + return cur + end + + delete = old.keys - new.keys + warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty? + delete.each{|k| cur.delete k } + + update = new.keys.select{|k| new[k] != old[k] } + warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty? + update.each{|k| cur[k] = new[k] } + + cur + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showexceptions.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showexceptions.rb new file mode 100644 index 0000000..697bc41 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showexceptions.rb @@ -0,0 +1,349 @@ +require 'ostruct' +require 'erb' +require 'rack/request' +require 'rack/utils' + +module Rack + # Rack::ShowExceptions catches all exceptions raised from the app it + # wraps. It shows a useful backtrace with the sourcefile and + # clickable context, the whole Rack environment and the request + # data. + # + # Be careful when you use this on public-facing sites as it could + # reveal information helpful to attackers. + + class ShowExceptions + CONTEXT = 7 + + def initialize(app) + @app = app + @template = ERB.new(TEMPLATE) + end + + def call(env) + @app.call(env) + rescue StandardError, LoadError, SyntaxError => e + backtrace = pretty(env, e) + [500, + {"Content-Type" => "text/html", + "Content-Length" => backtrace.join.size.to_s}, + backtrace] + end + + def pretty(env, exception) + req = Rack::Request.new(env) + path = (req.script_name + req.path_info).squeeze("/") + + frames = exception.backtrace.map { |line| + frame = OpenStruct.new + if line =~ /(.*?):(\d+)(:in `(.*)')?/ + frame.filename = $1 + frame.lineno = $2.to_i + frame.function = $4 + + begin + lineno = frame.lineno-1 + lines = ::File.readlines(frame.filename) + frame.pre_context_lineno = [lineno-CONTEXT, 0].max + frame.pre_context = lines[frame.pre_context_lineno...lineno] + frame.context_line = lines[lineno].chomp + frame.post_context_lineno = [lineno+CONTEXT, lines.size].min + frame.post_context = lines[lineno+1..frame.post_context_lineno] + rescue + end + + frame + else + nil + end + }.compact + + env["rack.errors"].puts "#{exception.class}: #{exception.message}" + env["rack.errors"].puts exception.backtrace.map { |l| "\t" + l } + env["rack.errors"].flush + + [@template.result(binding)] + end + + def h(obj) # :nodoc: + case obj + when String + Utils.escape_html(obj) + else + Utils.escape_html(obj.inspect) + end + end + + # :stopdoc: + +# adapted from Django +# Copyright (c) 2005, the Lawrence Journal-World +# Used under the modified BSD license: +# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 +TEMPLATE = <<'HTML' + + + + + + <%=h exception.class %> at <%=h path %> + + + + + +
+

<%=h exception.class %> at <%=h path %>

+

<%=h exception.message %>

+ + + + + + +
Ruby<%=h frames.first.filename %>: in <%=h frames.first.function %>, line <%=h frames.first.lineno %>
Web<%=h req.request_method %> <%=h(req.host + path)%>
+ +

Jump to:

+ +
+ +
+

Traceback (innermost first)

+
    +<% frames.each { |frame| %> +
  • + <%=h frame.filename %>: in <%=h frame.function %> + + <% if frame.context_line %> +
    + <% if frame.pre_context %> +
      + <% frame.pre_context.each { |line| %> +
    1. <%=h line %>
    2. + <% } %> +
    + <% end %> + +
      +
    1. <%=h frame.context_line %>...
    + + <% if frame.post_context %> +
      + <% frame.post_context.each { |line| %> +
    1. <%=h line %>
    2. + <% } %> +
    + <% end %> +
    + <% end %> +
  • +<% } %> +
+
+ +
+

Request information

+ +

GET

+ <% unless req.GET.empty? %> + + + + + + + + + <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %> + + + + + <% } %> + +
VariableValue
<%=h key %>
<%=h val.inspect %>
+ <% else %> +

No GET data.

+ <% end %> + +

POST

+ <% unless req.POST.empty? %> + + + + + + + + + <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %> + + + + + <% } %> + +
VariableValue
<%=h key %>
<%=h val.inspect %>
+ <% else %> +

No POST data.

+ <% end %> + + + + <% unless req.cookies.empty? %> + + + + + + + + + <% req.cookies.each { |key, val| %> + + + + + <% } %> + +
VariableValue
<%=h key %>
<%=h val.inspect %>
+ <% else %> +

No cookie data.

+ <% end %> + +

Rack ENV

+ + + + + + + + + <% env.sort_by { |k, v| k.to_s }.each { |key, val| %> + + + + + <% } %> + +
VariableValue
<%=h key %>
<%=h val %>
+ +
+ +
+

+ You're seeing this error because you use Rack::ShowExceptions. +

+
+ + + +HTML + + # :startdoc: + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showstatus.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showstatus.rb new file mode 100644 index 0000000..28258c7 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showstatus.rb @@ -0,0 +1,106 @@ +require 'erb' +require 'rack/request' +require 'rack/utils' + +module Rack + # Rack::ShowStatus catches all empty responses the app it wraps and + # replaces them with a site explaining the error. + # + # Additional details can be put into rack.showstatus.detail + # and will be shown as HTML. If such details exist, the error page + # is always rendered, even if the reply was not empty. + + class ShowStatus + def initialize(app) + @app = app + @template = ERB.new(TEMPLATE) + end + + def call(env) + status, headers, body = @app.call(env) + headers = Utils::HeaderHash.new(headers) + empty = headers['Content-Length'].to_i <= 0 + + # client or server error, or explicit message + if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"] + req = Rack::Request.new(env) + message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s + detail = env["rack.showstatus.detail"] || message + body = @template.result(binding) + size = Rack::Utils.bytesize(body) + [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]] + else + [status, headers, body] + end + end + + def h(obj) # :nodoc: + case obj + when String + Utils.escape_html(obj) + else + Utils.escape_html(obj.inspect) + end + end + + # :stopdoc: + +# adapted from Django +# Copyright (c) 2005, the Lawrence Journal-World +# Used under the modified BSD license: +# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 +TEMPLATE = <<'HTML' + + + + + <%=h message %> at <%=h req.script_name + req.path_info %> + + + + +
+

<%=h message %> (<%= status.to_i %>)

+ + + + + + + + + +
Request Method:<%=h req.request_method %>
Request URL:<%=h req.url %>
+
+
+

<%= detail %>

+
+ +
+

+ You're seeing this error because you use Rack::ShowStatus. +

+
+ + +HTML + + # :startdoc: + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/static.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/static.rb new file mode 100644 index 0000000..168e8f8 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/static.rb @@ -0,0 +1,38 @@ +module Rack + + # The Rack::Static middleware intercepts requests for static files + # (javascript files, images, stylesheets, etc) based on the url prefixes + # passed in the options, and serves them using a Rack::File object. This + # allows a Rack stack to serve both static and dynamic content. + # + # Examples: + # use Rack::Static, :urls => ["/media"] + # will serve all requests beginning with /media from the "media" folder + # located in the current directory (ie media/*). + # + # use Rack::Static, :urls => ["/css", "/images"], :root => "public" + # will serve all requests beginning with /css or /images from the folder + # "public" in the current directory (ie public/css/* and public/images/*) + + class Static + + def initialize(app, options={}) + @app = app + @urls = options[:urls] || ["/favicon.ico"] + root = options[:root] || Dir.pwd + @file_server = Rack::File.new(root) + end + + def call(env) + path = env["PATH_INFO"] + can_serve = @urls.any? { |url| path.index(url) == 0 } + + if can_serve + @file_server.call(env) + else + @app.call(env) + end + end + + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/urlmap.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/urlmap.rb new file mode 100644 index 0000000..fcf6616 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/urlmap.rb @@ -0,0 +1,55 @@ +module Rack + # Rack::URLMap takes a hash mapping urls or paths to apps, and + # dispatches accordingly. Support for HTTP/1.1 host names exists if + # the URLs start with http:// or https://. + # + # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part + # relevant for dispatch is in the SCRIPT_NAME, and the rest in the + # PATH_INFO. This should be taken care of when you need to + # reconstruct the URL in order to create links. + # + # URLMap dispatches in such a way that the longest paths are tried + # first, since they are most specific. + + class URLMap + def initialize(map = {}) + remap(map) + end + + def remap(map) + @mapping = map.map { |location, app| + if location =~ %r{\Ahttps?://(.*?)(/.*)} + host, location = $1, $2 + else + host = nil + end + + unless location[0] == ?/ + raise ArgumentError, "paths need to start with /" + end + location = location.chomp('/') + + [host, location, app] + }.sort_by { |(h, l, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first + end + + def call(env) + path = env["PATH_INFO"].to_s.squeeze("/") + script_name = env['SCRIPT_NAME'] + hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT') + @mapping.each { |host, location, app| + next unless (hHost == host || sName == host \ + || (host.nil? && (hHost == sName || hHost == sName+':'+sPort))) + next unless location == path[0, location.size] + next unless path[location.size] == nil || path[location.size] == ?/ + + return app.call( + env.merge( + 'SCRIPT_NAME' => (script_name + location), + 'PATH_INFO' => path[location.size..-1])) + } + [404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]] + end + end +end + diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/utils.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/utils.rb new file mode 100644 index 0000000..42e2e69 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/utils.rb @@ -0,0 +1,516 @@ +# -*- encoding: binary -*- + +require 'set' +require 'tempfile' + +module Rack + # Rack::Utils contains a grab-bag of useful methods for writing web + # applications adopted from all kinds of Ruby libraries. + + module Utils + # Performs URI escaping so that you can construct proper + # query strings faster. Use this rather than the cgi.rb + # version since it's faster. (Stolen from Camping). + def escape(s) + s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) { + '%'+$1.unpack('H2'*$1.size).join('%').upcase + }.tr(' ', '+') + end + module_function :escape + + # Unescapes a URI escaped string. (Stolen from Camping). + def unescape(s) + s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ + [$1.delete('%')].pack('H*') + } + end + module_function :unescape + + # Stolen from Mongrel, with some small modifications: + # Parses a query string by breaking it up at the '&' + # and ';' characters. You can also use this to parse + # cookies by changing the characters used in the second + # parameter (which defaults to '&;'). + def parse_query(qs, d = '&;') + params = {} + + (qs || '').split(/[#{d}] */n).each do |p| + k, v = unescape(p).split('=', 2) + + if cur = params[k] + if cur.class == Array + params[k] << v + else + params[k] = [cur, v] + end + else + params[k] = v + end + end + + return params + end + module_function :parse_query + + def parse_nested_query(qs, d = '&;') + params = {} + + (qs || '').split(/[#{d}] */n).each do |p| + k, v = unescape(p).split('=', 2) + normalize_params(params, k, v) + end + + return params + end + module_function :parse_nested_query + + def normalize_params(params, name, v = nil) + name =~ %r(\A[\[\]]*([^\[\]]+)\]*) + k = $1 || '' + after = $' || '' + + return if k.empty? + + if after == "" + params[k] = v + elsif after == "[]" + params[k] ||= [] + raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) + params[k] << v + elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) + child_key = $1 + params[k] ||= [] + raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) + if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) + normalize_params(params[k].last, child_key, v) + else + params[k] << normalize_params({}, child_key, v) + end + else + params[k] ||= {} + raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) + params[k] = normalize_params(params[k], after, v) + end + + return params + end + module_function :normalize_params + + def build_query(params) + params.map { |k, v| + if v.class == Array + build_query(v.map { |x| [k, x] }) + else + escape(k) + "=" + escape(v) + end + }.join("&") + end + module_function :build_query + + def build_nested_query(value, prefix = nil) + case value + when Array + value.map { |v| + build_nested_query(v, "#{prefix}[]") + }.join("&") + when Hash + value.map { |k, v| + build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) + }.join("&") + when String + raise ArgumentError, "value must be a Hash" if prefix.nil? + "#{prefix}=#{escape(value)}" + else + prefix + end + end + module_function :build_nested_query + + # Escape ampersands, brackets and quotes to their HTML/XML entities. + def escape_html(string) + string.to_s.gsub("&", "&"). + gsub("<", "<"). + gsub(">", ">"). + gsub("'", "'"). + gsub('"', """) + end + module_function :escape_html + + def select_best_encoding(available_encodings, accept_encoding) + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + + expanded_accept_encoding = + accept_encoding.map { |m, q| + if m == "*" + (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] } + else + [[m, q]] + end + }.inject([]) { |mem, list| + mem + list + } + + encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m } + + unless encoding_candidates.include?("identity") + encoding_candidates.push("identity") + end + + expanded_accept_encoding.find_all { |m, q| + q == 0.0 + }.each { |m, _| + encoding_candidates.delete(m) + } + + return (encoding_candidates & available_encodings)[0] + end + module_function :select_best_encoding + + # Return the bytesize of String; uses String#length under Ruby 1.8 and + # String#bytesize under 1.9. + if ''.respond_to?(:bytesize) + def bytesize(string) + string.bytesize + end + else + def bytesize(string) + string.size + end + end + module_function :bytesize + + # Context allows the use of a compatible middleware at different points + # in a request handling stack. A compatible middleware must define + # #context which should take the arguments env and app. The first of which + # would be the request environment. The second of which would be the rack + # application that the request would be forwarded to. + class Context + attr_reader :for, :app + + def initialize(app_f, app_r) + raise 'running context does not respond to #context' unless app_f.respond_to? :context + @for, @app = app_f, app_r + end + + def call(env) + @for.context(env, @app) + end + + def recontext(app) + self.class.new(@for, app) + end + + def context(env, app=@app) + recontext(app).call(env) + end + end + + # A case-insensitive Hash that preserves the original case of a + # header when set. + class HeaderHash < Hash + def initialize(hash={}) + @names = {} + hash.each { |k, v| self[k] = v } + end + + def to_hash + inject({}) do |hash, (k,v)| + if v.respond_to? :to_ary + hash[k] = v.to_ary.join("\n") + else + hash[k] = v + end + hash + end + end + + def [](k) + super @names[k.downcase] + end + + def []=(k, v) + delete k + @names[k.downcase] = k + super k, v + end + + def delete(k) + super @names.delete(k.downcase) + end + + def include?(k) + @names.has_key? k.downcase + end + + alias_method :has_key?, :include? + alias_method :member?, :include? + alias_method :key?, :include? + + def merge!(other) + other.each { |k, v| self[k] = v } + self + end + + def merge(other) + hash = dup + hash.merge! other + end + end + + # Every standard HTTP code mapped to the appropriate message. + # Stolen from Mongrel. + HTTP_STATUS_CODES = { + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + } + + # Responses with HTTP status codes that should not have an entity body + STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304) + + # A multipart form data parser, adapted from IOWA. + # + # Usually, Rack::Request#POST takes care of calling this. + + module Multipart + class UploadedFile + # The filename, *not* including the path, of the "uploaded" file + attr_reader :original_filename + + # The content type of the "uploaded" file + attr_accessor :content_type + + def initialize(path, content_type = "text/plain", binary = false) + raise "#{path} file does not exist" unless ::File.exist?(path) + @content_type = content_type + @original_filename = ::File.basename(path) + @tempfile = Tempfile.new(@original_filename) + @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding) + @tempfile.binmode if binary + FileUtils.copy_file(path, @tempfile.path) + end + + def path + @tempfile.path + end + alias_method :local_path, :path + + def method_missing(method_name, *args, &block) #:nodoc: + @tempfile.__send__(method_name, *args, &block) + end + end + + EOL = "\r\n" + MULTIPART_BOUNDARY = "AaB03x" + + def self.parse_multipart(env) + unless env['CONTENT_TYPE'] =~ + %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n + nil + else + boundary = "--#{$1}" + + params = {} + buf = "" + content_length = env['CONTENT_LENGTH'].to_i + input = env['rack.input'] + input.rewind + + boundary_size = Utils.bytesize(boundary) + EOL.size + bufsize = 16384 + + content_length -= boundary_size + + read_buffer = '' + + status = input.read(boundary_size, read_buffer) + raise EOFError, "bad content body" unless status == boundary + EOL + + rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n + + loop { + head = nil + body = '' + filename = content_type = name = nil + + until head && buf =~ rx + if !head && i = buf.index(EOL+EOL) + head = buf.slice!(0, i+2) # First \r\n + buf.slice!(0, 2) # Second \r\n + + filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1] + content_type = head[/Content-Type: (.*)#{EOL}/ni, 1] + name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1] + + if content_type || filename + body = Tempfile.new("RackMultipart") + body.binmode if body.respond_to?(:binmode) + end + + next + end + + # Save the read body part. + if head && (boundary_size+4 < buf.size) + body << buf.slice!(0, buf.size - (boundary_size+4)) + end + + c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer) + raise EOFError, "bad content body" if c.nil? || c.empty? + buf << c + content_length -= c.size + end + + # Save the rest. + if i = buf.index(rx) + body << buf.slice!(0, i) + buf.slice!(0, boundary_size+2) + + content_length = -1 if $1 == "--" + end + + if filename == "" + # filename is blank which means no file has been selected + data = nil + elsif filename + body.rewind + + # Take the basename of the upload's original filename. + # This handles the full Windows paths given by Internet Explorer + # (and perhaps other broken user agents) without affecting + # those which give the lone filename. + filename =~ /^(?:.*[:\\\/])?(.*)/m + filename = $1 + + data = {:filename => filename, :type => content_type, + :name => name, :tempfile => body, :head => head} + elsif !filename && content_type + body.rewind + + # Generic multipart cases, not coming from a form + data = {:type => content_type, + :name => name, :tempfile => body, :head => head} + else + data = body + end + + Utils.normalize_params(params, name, data) unless data.nil? + + break if buf.empty? || content_length == -1 + } + + input.rewind + + params + end + end + + def self.build_multipart(params, first = true) + if first + unless params.is_a?(Hash) + raise ArgumentError, "value must be a Hash" + end + + multipart = false + query = lambda { |value| + case value + when Array + value.each(&query) + when Hash + value.values.each(&query) + when UploadedFile + multipart = true + end + } + params.values.each(&query) + return nil unless multipart + end + + flattened_params = Hash.new + + params.each do |key, value| + k = first ? key.to_s : "[#{key}]" + + case value + when Array + value.map { |v| + build_multipart(v, false).each { |subkey, subvalue| + flattened_params["#{k}[]#{subkey}"] = subvalue + } + } + when Hash + build_multipart(value, false).each { |subkey, subvalue| + flattened_params[k + subkey] = subvalue + } + else + flattened_params[k] = value + end + end + + if first + flattened_params.map { |name, file| + if file.respond_to?(:original_filename) + ::File.open(file.path, "rb") do |f| + f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding) +<<-EOF +--#{MULTIPART_BOUNDARY}\r +Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r +Content-Type: #{file.content_type}\r +Content-Length: #{::File.stat(file.path).size}\r +\r +#{f.read}\r +EOF + end + else +<<-EOF +--#{MULTIPART_BOUNDARY}\r +Content-Disposition: form-data; name="#{name}"\r +\r +#{file}\r +EOF + end + }.join + "--#{MULTIPART_BOUNDARY}--\r" + else + flattened_params + end + end + end + end +end -- 1.6.4.2 From 056ddbdcfb07f0b5c7e6ed8a35f6c3b55b4ab489 Mon Sep 17 00:00:00 2001 From: nate Date: Tue, 26 May 2009 15:06:09 -0500 Subject: [PATCH 102/779] A test to show that http_authentication needs to fail authentication if the password procedure returns nil. Also includes a fix to validate_digest_response to fail validation if the password procedure returns nil. Signed-off-by: Michael Koziarski --- .../lib/action_controller/http_authentication.rb | 4 +++- .../controller/http_digest_authentication_test.rb | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index eb64525..88c289b 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -183,7 +183,7 @@ module ActionController request.env['REDIRECT_X_HTTP_AUTHORIZATION'] end - # Raises error unless the request credentials response value matches the expected value. + # Returns false unless the request credentials response value matches the expected value. # First try the password as a ha1 digest password. If this fails, then try it as a plain # text password. def validate_digest_response(request, realm, &password_procedure) @@ -192,6 +192,8 @@ module ActionController if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque] password = password_procedure.call(credentials[:username]) + return false unless password + method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] [true, false].any? do |password_is_ha1| diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index a725f7c..5cc47ee 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -67,6 +67,15 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'SuperSecret', credentials[:realm] end + test "authentication request with nil credentials" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => nil, :password => nil) + get :index + + assert_response :unauthorized + assert_equal "HTTP Digest: Access denied.\n", @response.body, "Authentication didn't fail for request" + assert_not_equal 'Hello Secret', @response.body, "Authentication didn't fail for request" + end + test "authentication request with invalid password" do @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'foo') get :display @@ -161,6 +170,11 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'Definitely Maybe', @response.body end + test "validate_digest_response should fail with nil returning password_procedure" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => nil, :password => nil) + assert !ActionController::HttpAuthentication::Digest.validate_digest_response(@request, "SuperSecret"){nil} + end + private def encode_credentials(options) -- 1.6.4.2 From 2c3d2906b255b1cb3aeb5d3abb9fe2dcb2872c28 Mon Sep 17 00:00:00 2001 From: Matt Jones Date: Sat, 6 Jun 2009 18:05:38 -0400 Subject: [PATCH 103/779] Fix several issues with the 2.3.2 gem loader. Incorporates the following: - migrates back small change to gems:build:force from bfc1609a501fc3ed442685819de5bcdb5fbada1c to finish closing #2266. - unrolls to_proc calls in gems.rake, to match the change in master. - fixes #2722 by passing the options hash to dependencies during build. (includes a test) - fixes #2721 by loading the specification directly in from_directory_name. Adds an option to opt-out of specification loading when needed (in gems:refresh_specs, for instance). Includes tests. - fixes #2679 by refreshing specs for all frozen gems rather than just gems loaded from the environment. - fixes #2678 by passing the options hash to dependencies during unpack. Signed-off-by: Michael Koziarski --- railties/lib/rails/gem_dependency.rb | 20 ++++++++++++++++---- railties/lib/tasks/gems.rake | 17 ++++++++--------- railties/test/gem_dependency_test.rb | 18 ++++++++++++++++-- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb index ee3d0d8..3a82202 100644 --- a/railties/lib/rails/gem_dependency.rb +++ b/railties/lib/rails/gem_dependency.rb @@ -29,11 +29,18 @@ module Rails end end - def self.from_directory_name(directory_name) + def self.from_directory_name(directory_name, load_spec=true) directory_name_parts = File.basename(directory_name).split('-') name = directory_name_parts[0..-2].join('-') version = directory_name_parts.last - self.new(name, :version => version) + result = self.new(name, :version => version) + spec_filename = File.join(unpacked_path, directory_name, '.specification') + if load_spec + raise "Missing specification file in #{File.dirname(spec_filename)}. Perhaps you need to do a 'rake gems:refresh_specs'?" unless File.exists?(spec_filename) + spec = YAML::load_file(spec_filename) + result.specification = spec + end + result rescue ArgumentError => e raise "Unable to determine gem name and version from '#{directory_name}'" end @@ -104,6 +111,10 @@ module Rails end end + def specification=(s) + @spec = s + end + def requirement r = version_requirements (r == Gem::Requirement.default) ? nil : r @@ -170,13 +181,14 @@ module Rails def build(options={}) require 'rails/gem_builder' + return if specification.nil? if options[:force] || !built? return unless File.exists?(unpacked_specification_filename) spec = YAML::load_file(unpacked_specification_filename) Rails::GemBuilder.new(spec, unpacked_gem_directory).build_extensions puts "Built gem: '#{unpacked_gem_directory}'" end - dependencies.each { |dep| dep.build } + dependencies.each { |dep| dep.build(options) } end def install @@ -236,7 +248,7 @@ module Rails real_spec = Gem::Specification.load(specification.loaded_from) write_specification(real_spec) end - dependencies.each { |dep| dep.unpack } if options[:recursive] + dependencies.each { |dep| dep.unpack(options) } if options[:recursive] end def write_specification(spec) diff --git a/railties/lib/tasks/gems.rake b/railties/lib/tasks/gems.rake index efadb1d..f1c34c7 100644 --- a/railties/lib/tasks/gems.rake +++ b/railties/lib/tasks/gems.rake @@ -20,26 +20,25 @@ 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 desc "Force the build of all gems" task :force do $gems_build_rake_task = true - Rake::Task['gems:unpack'].invoke - current_gems.each { |gem| gem.build(:force => true) } + frozen_gems.each { |gem| gem.build(:force => true) } end end 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,8 +49,8 @@ namespace :gems do end desc "Regenerate gem specifications in correct format." - task :refresh_specs => :base do - current_gems.each &:refresh + task :refresh_specs do + frozen_gems(false).each { |gem| gem.refresh } end end @@ -61,9 +60,9 @@ def current_gems gems end -def frozen_gems +def frozen_gems(load_specs=true) Dir[File.join(RAILS_ROOT, 'vendor', 'gems', '*-*')].map do |gem_dir| - Rails::GemDependency.from_directory_name(gem_dir) + Rails::GemDependency.from_directory_name(gem_dir, load_specs) end end diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb index 3853eb5..52d6ad9 100644 --- a/railties/test/gem_dependency_test.rb +++ b/railties/test/gem_dependency_test.rb @@ -18,7 +18,7 @@ class GemDependencyTest < Test::Unit::TestCase def test_configuration_adds_gem_dependency config = Rails::Configuration.new config.gem "xaws-s3x", :lib => "aws/s3", :version => "0.4.0" - assert_equal [["install", "xaws-s3x", "--version", '"= 0.4.0"']], config.gems.collect(&:install_command) + assert_equal [["install", "xaws-s3x", "--version", '"= 0.4.0"']], config.gems.collect { |g| g.install_command } end def test_gem_creates_install_command @@ -165,8 +165,14 @@ class GemDependencyTest < Test::Unit::TestCase dummy_gem.unpack end + def test_gem_from_directory_name_attempts_to_load_specification + assert_raises RuntimeError do + dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1') + end + end + def test_gem_from_directory_name - dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1') + dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1', false) assert_equal 'dummy-gem', dummy_gem.name assert_equal '= 1.1', dummy_gem.version_requirements.to_s end @@ -186,4 +192,12 @@ class GemDependencyTest < Test::Unit::TestCase assert_equal false, Rails::GemDependency.new("dummy-gem-j").built? end + def test_gem_build_passes_options_to_dependencies + start_gem = Rails::GemDependency.new("dummy-gem-g") + dep_gem = Rails::GemDependency.new("dummy-gem-f") + start_gem.stubs(:dependencies).returns([dep_gem]) + dep_gem.expects(:build).with({ :force => true }).once + start_gem.build(:force => true) + end + end -- 1.6.4.2 From b1a044b6291b1045e31dc624189f38a3abc4cda2 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Tue, 9 Jun 2009 20:03:36 +1200 Subject: [PATCH 104/779] Revert "Ensure HasManyThroughAssociation#destroy delete orphan records" This reverts commit 7a85927da21859a6868c3e0ec92267706b0a14bf. There's still some debate about the intended behaviour in the ticket, leaving in master but removing prior to shipping 2.3.3 --- .../associations/has_many_through_association.rb | 7 ------- .../has_many_through_associations_test.rb | 10 ++-------- 2 files changed, 2 insertions(+), 15 deletions(-) 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 efd96e3..1c091e7 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -22,13 +22,6 @@ 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/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 51c70b9..97efca7 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -93,7 +93,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end def test_destroy_association - assert_difference ["Person.count", "Reader.count"], -1 do + assert_difference "Person.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", "Reader.count"], -1 do + assert_difference "Person.count", -1 do posts(:welcome).people.destroy_all end @@ -110,12 +110,6 @@ 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.6.4.2 From d97073337c7b9012bad4d65ef61ce8f07f8179aa Mon Sep 17 00:00:00 2001 From: Tom Ward Date: Fri, 22 May 2009 15:27:01 +0100 Subject: [PATCH 105/779] Change autoload declaration in ActionView::Helpers from JavascriptHelper to JavascriptHelper, matching the actual helper name. Also removed require from UrlHelper which was inadvertently preventing the autoload typo from causing a failure. Signed-off-by: Michael Koziarski --- actionpack/lib/action_view/helpers.rb | 2 +- actionpack/lib/action_view/helpers/url_helper.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb index 693ab7c..97fa2d8 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -11,7 +11,7 @@ module ActionView #:nodoc: autoload :FormHelper, 'action_view/helpers/form_helper' autoload :FormOptionsHelper, 'action_view/helpers/form_options_helper' autoload :FormTagHelper, 'action_view/helpers/form_tag_helper' - autoload :JavascriptHelper, 'action_view/helpers/javascript_helper' + autoload :JavaScriptHelper, 'action_view/helpers/javascript_helper' autoload :NumberHelper, 'action_view/helpers/number_helper' autoload :PrototypeHelper, 'action_view/helpers/prototype_helper' autoload :RecordIdentificationHelper, 'action_view/helpers/record_identification_helper' diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 36e0a78..9270886 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -1,4 +1,4 @@ -require 'action_view/helpers/javascript_helper' +#require 'action_view/helpers/javascript_helper' module ActionView module Helpers #:nodoc: -- 1.6.4.2 From c5c022c705e4179f74e063186ea68a1c58797822 Mon Sep 17 00:00:00 2001 From: Eugene Pimenov Date: Thu, 23 Apr 2009 13:45:12 +0400 Subject: [PATCH 106/779] PostgreSQL adapter should call thread safe quote_string function Signed-off-by: Michael Koziarski --- .../connection_adapters/postgresql_adapter.rb | 16 ++++++++++++++-- 1 files changed, 14 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 4961793..8440223 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -287,7 +287,13 @@ module ActiveRecord # Escapes binary strings for bytea input to the database. def escape_bytea(value) - if PGconn.respond_to?(:escape_bytea) + if @connection.respond_to?(:escape_bytea) + self.class.instance_eval do + define_method(:escape_bytea) do |value| + @connection.escape_bytea(value) if value + end + end + elsif PGconn.respond_to?(:escape_bytea) self.class.instance_eval do define_method(:escape_bytea) do |value| PGconn.escape_bytea(value) if value @@ -376,7 +382,13 @@ module ActiveRecord # Quotes strings for use in SQL input in the postgres driver for better performance. def quote_string(s) #:nodoc: - if PGconn.respond_to?(:escape) + if @connection.respond_to?(:escape) + self.class.instance_eval do + define_method(:quote_string) do |s| + @connection.escape(s) + end + end + elsif PGconn.respond_to?(:escape) self.class.instance_eval do define_method(:quote_string) do |s| PGconn.escape(s) -- 1.6.4.2 From d63fab344f5321ea71198ac349fea1ee15773f7d Mon Sep 17 00:00:00 2001 From: Steven Luscher Date: Mon, 6 Apr 2009 20:47:23 -0700 Subject: [PATCH 107/779] Fixes #2439. ActionController::Integration::Session no longer mangles multiparameter attribute params when processing multipart requests. Signed-off-by: Michael Koziarski --- actionpack/lib/action_controller/integration.rb | 2 +- actionpack/test/controller/integration_test.rb | 28 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 5178496..58b6476 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -409,7 +409,7 @@ module ActionController def multipart_requestify(params, first=true) returning Hash.new do |p| params.each do |key, value| - k = first ? CGI.escape(key.to_s) : "[#{CGI.escape(key.to_s)}]" + k = first ? key.to_s : "[#{key.to_s}]" if Hash === value multipart_requestify(value, false).each do |subkey, subvalue| p[k + subkey] = subvalue diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index e39a934..b8281b5 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -240,6 +240,14 @@ class IntegrationProcessTest < ActionController::IntegrationTest render :text => "foo: #{params[:foo]}", :status => 200 end + def post_with_multiparameter_params + render :text => "foo(1i): #{params[:"foo(1i)"]}, foo(2i): #{params[:"foo(2i)"]}", :status => 200 + end + + def multipart_post_with_multiparameter_params + render :text => "foo(1i): #{params[:"foo(1i)"]}, foo(2i): #{params[:"foo(2i)"]}, filesize: #{params[:file].size}", :status => 200 + end + def post render :text => "Created", :status => 201 end @@ -255,6 +263,8 @@ class IntegrationProcessTest < ActionController::IntegrationTest end end + FILES_DIR = File.dirname(__FILE__) + '/../fixtures/multipart' + def test_get with_test_route_set do get '/get' @@ -360,6 +370,24 @@ class IntegrationProcessTest < ActionController::IntegrationTest end end + def test_post_with_multiparameter_attribute_parameters + with_test_route_set do + post '/post_with_multiparameter_params', :"foo(1i)" => "bar", :"foo(2i)" => "baz" + + assert_equal 200, status + assert_equal "foo(1i): bar, foo(2i): baz", response.body + end + end + + def test_multipart_post_with_multiparameter_attribute_parameters + with_test_route_set do + post '/multipart_post_with_multiparameter_params', :"foo(1i)" => "bar", :"foo(2i)" => "baz", :file => fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg") + + assert_equal 200, status + assert_equal "foo(1i): bar, foo(2i): baz, filesize: 159528", response.body + end + end + def test_head with_test_route_set do head '/get' -- 1.6.4.2 From 72d111a21ca21460a320d7611b1fdf07d01ffeb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedrich=20G=C3=B6pel?= Date: Tue, 9 Jun 2009 20:22:20 +1200 Subject: [PATCH 108/779] 1.9 compatibility - don't pass an array as the from address as this ends up generating invalid SMTP commands. --- actionmailer/lib/action_mailer/base.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index b77409b..000c05d 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -674,7 +674,7 @@ module ActionMailer #:nodoc: def perform_delivery_smtp(mail) destinations = mail.destinations mail.ready_to_send - sender = (mail['return-path'] && mail['return-path'].spec) || mail.from + sender = (mail['return-path'] && mail['return-path'].spec) || mail['from'] smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port]) smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto) -- 1.6.4.2 From 898a8801ff547d18c349f069cd696926c27174e8 Mon Sep 17 00:00:00 2001 From: David Stevenson Date: Wed, 11 Mar 2009 09:28:56 -0700 Subject: [PATCH 109/779] Made label target radio button tags with values. Radio button now respects inherited :index options when generating id. Signed-off-by: Michael Koziarski --- actionpack/lib/action_view/helpers/form_helper.rb | 26 +++++++++++++++----- actionpack/test/template/form_helper_test.rb | 19 +++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index a589bcb..6d30182 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -493,7 +493,8 @@ module ActionView # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify # it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged - # onto the HTML as an HTML element attribute as in the example shown. + # onto the HTML as an HTML element attribute as in the example shown, except for the :value option, which is designed to + # target labels for radio_button tags (where the value is used in the ID of the input tag). # # ==== Examples # label(:post, :title) @@ -505,6 +506,9 @@ module ActionView # label(:post, :title, "A short title", :class => "title_label") # # => # + # label(:post, :privacy, "Public Post", :value => "public") + # # => + # def label(object_name, method, text = nil, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options) end @@ -720,8 +724,9 @@ module ActionView def to_label_tag(text = nil, options = {}) options = options.stringify_keys + tag_value = options.delete("value") name_and_id = options.dup - add_default_name_and_id(name_and_id) + add_default_name_and_id_for_value(tag_value, name_and_id) options.delete("index") options["for"] ||= name_and_id["id"] content = (text.blank? ? nil : text.to_s) || method_name.humanize @@ -753,11 +758,7 @@ module ActionView checked = self.class.radio_button_checked?(value(object), tag_value) end options["checked"] = "checked" if checked - pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase - options["id"] ||= defined?(@auto_index) ? - "#{tag_id_with_index(@auto_index)}_#{pretty_tag_value}" : - "#{tag_id}_#{pretty_tag_value}" - add_default_name_and_id(options) + add_default_name_and_id_for_value(tag_value, options) tag("input", options) end @@ -858,6 +859,17 @@ module ActionView end private + def add_default_name_and_id_for_value(tag_value, options) + if tag_value + pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase + specified_id = options["id"] + add_default_name_and_id(options) + options["id"] += "_#{pretty_tag_value}" unless specified_id + else + add_default_name_and_id(options) + end + end + def add_default_name_and_id(options) if options.has_key?("index") options["name"] ||= tag_name_with_index(options["index"]) diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 654eee4..81c5cb3 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -98,6 +98,11 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal('', label(:post, :title, nil, "for" => "my_for")) end + def test_label_for_radio_buttons_with_value + assert_dom_equal('', label("post", "title", "The title goes here", :value => "great_title")) + assert_dom_equal('', label("post", "title", "The title goes here", :value => "great title")) + end + def test_text_field assert_dom_equal( '', text_field("post", "title") @@ -531,6 +536,20 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_index_radio_button + form_for(:post, @post) do |f| + f.fields_for(:comment, @post, :index => 5) do |c| + concat c.radio_button(:title, "hello") + end + end + + expected = "
" + + "" + + "
" + + assert_dom_equal expected, output_buffer + end + def test_nested_fields_for_with_auto_index_on_both form_for("post[]", @post) do |f| f.fields_for("comment[]", @post) do |c| -- 1.6.4.2 From cd14a4a00e1005c4adcf36d2f171fb52a3c08f4f Mon Sep 17 00:00:00 2001 From: Stephen Anderson Date: Tue, 6 Jan 2009 11:28:40 -0600 Subject: [PATCH 110/779] Sanitized the id generated by text_area_tag helper method. text_area_tag('item[description]') should return: instead of: The old id was causing HTML validation failures. Signed-off-by: Michael Koziarski --- .../lib/action_view/helpers/form_tag_helper.rb | 2 +- actionpack/test/template/form_tag_helper_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index f5f26f1..f1cc027 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -257,7 +257,7 @@ module ActionView options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split) end - content_tag :textarea, content, { "name" => name, "id" => name }.update(options.stringify_keys) + content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end # Creates a check box form input tag. diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index e3f64cd..e3a140a 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -153,6 +153,11 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal expected, actual end + def test_text_area_tag_id_sanitized + input_elem = root_elem(text_area_tag("item[][description]")) + assert_match VALID_HTML_ID, input_elem['id'] + end + def test_text_field_tag actual = text_field_tag "title", "Hello!" expected = %() -- 1.6.4.2 From f43404d42b28c9c4e126829de5f9d815618abe5b Mon Sep 17 00:00:00 2001 From: Matt Jones Date: Tue, 9 Jun 2009 12:24:23 -0400 Subject: [PATCH 111/779] Fix incorrect specification path in GemDependency#from_directory_name Signed-off-by: Michael Koziarski --- railties/lib/rails/gem_dependency.rb | 2 +- railties/test/gem_dependency_test.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletions(-) diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb index 3a82202..3cc7549 100644 --- a/railties/lib/rails/gem_dependency.rb +++ b/railties/lib/rails/gem_dependency.rb @@ -34,7 +34,7 @@ module Rails name = directory_name_parts[0..-2].join('-') version = directory_name_parts.last result = self.new(name, :version => version) - spec_filename = File.join(unpacked_path, directory_name, '.specification') + spec_filename = File.join(directory_name, '.specification') if load_spec raise "Missing specification file in #{File.dirname(spec_filename)}. Perhaps you need to do a 'rake gems:refresh_specs'?" unless File.exists?(spec_filename) spec = YAML::load_file(spec_filename) diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb index 52d6ad9..58b664d 100644 --- a/railties/test/gem_dependency_test.rb +++ b/railties/test/gem_dependency_test.rb @@ -177,6 +177,13 @@ class GemDependencyTest < Test::Unit::TestCase assert_equal '= 1.1', dummy_gem.version_requirements.to_s end + def test_gem_from_directory_name_loads_specification_successfully + assert_nothing_raised do + dummy_gem = Rails::GemDependency.from_directory_name(File.join(Rails::GemDependency.unpacked_path, 'dummy-gem-g-1.0.0')) + assert_not_nil dummy_gem.specification + end + end + def test_gem_from_invalid_directory_name assert_raises RuntimeError do dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem') -- 1.6.4.2 From 19c38a9b70c377c0a9e3b266f2b4023ff590ce02 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 10 Jun 2009 12:10:13 +1200 Subject: [PATCH 112/779] Whitelist the methods which are called by multiparameter attribute assignment. This prevents users from causing NoMethodErrors and the like by editing the parameter names, and closes a potential exploit of CVE-2009-1904. --- activerecord/lib/active_record/base.rb | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index adfb7ad..d155837 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -3029,11 +3029,11 @@ module ActiveRecord #:nodoc: def execute_callstack_for_multiparameter_attributes(callstack) errors = [] callstack.each do |name, values| - klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass - if values.empty? - send(name + "=", nil) - else - begin + begin + klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass + if values.empty? + send(name + "=", nil) + else value = if Time == klass instantiate_time_object(name, values) elsif Date == klass @@ -3047,9 +3047,9 @@ module ActiveRecord #:nodoc: end send(name + "=", value) - rescue => ex - errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name) end + rescue => ex + errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name) end end unless errors.empty? @@ -3075,7 +3075,7 @@ module ActiveRecord #:nodoc: end def type_cast_attribute_value(multiparameter_name, value) - multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send("to_" + $1) : value + multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value end def find_parameter_position(multiparameter_name) -- 1.6.4.2 From 7e1bcef98586327ff3f6d09862c4b29cb9925bcb Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 9 Jun 2009 23:51:04 -0700 Subject: [PATCH 113/779] Remove dead AbstractRequest autoload --- actionpack/lib/action_controller.rb | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 28702cb..187c958 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -45,7 +45,6 @@ module ActionController [Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter] end - autoload :AbstractRequest, 'action_controller/request' autoload :Base, 'action_controller/base' autoload :Benchmarking, 'action_controller/benchmarking' autoload :Caching, 'action_controller/caching' -- 1.6.4.2 From d3d4822262859cc773f28257bd1e5159b69ccb82 Mon Sep 17 00:00:00 2001 From: Andrew Kaspick Date: Thu, 4 Jun 2009 22:50:41 -0500 Subject: [PATCH 114/779] allow absolute paths for the asset caches Signed-off-by: Michael Koziarski --- .../lib/action_view/helpers/asset_tag_helper.rb | 6 +++--- actionpack/test/template/asset_tag_helper_test.rb | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index a32beb6..dffb708 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -277,7 +277,7 @@ module ActionView if ActionController::Base.perform_caching && cache joined_javascript_name = (cache == true ? "all" : cache) + ".js" - joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name) + joined_javascript_path = File.join(joined_javascript_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : JAVASCRIPTS_DIR, joined_javascript_name) write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path) javascript_src_tag(joined_javascript_name, options) @@ -417,7 +417,7 @@ module ActionView if ActionController::Base.perform_caching && cache joined_stylesheet_name = (cache == true ? "all" : cache) + ".css" - joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name) + joined_stylesheet_path = File.join(joined_stylesheet_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : STYLESHEETS_DIR, joined_stylesheet_name) write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path) stylesheet_tag(joined_stylesheet_name, options) @@ -679,4 +679,4 @@ module ActionView end end end -end \ No newline at end of file +end diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 32ad87c..7845746 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -316,9 +316,17 @@ class AssetTagHelperTest < ActionView::TestCase assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) + assert_dom_equal( + %(), + javascript_include_tag(:all, :cache => "/absolute/test") + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.js')) + ensure FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js')) FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute')) end def test_caching_javascript_include_tag_when_caching_on_with_proc_asset_host @@ -546,9 +554,17 @@ class AssetTagHelperTest < ActionView::TestCase ) assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + + assert_dom_equal( + %(), + stylesheet_link_tag(:all, :cache => "/absolute/test") + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.css')) ensure FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute')) end def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host -- 1.6.4.2 From 25fde77674cd6f9906b7a8e7eef046f06698c02a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 11 Jun 2009 19:39:21 -0500 Subject: [PATCH 115/779] Vendor rack 1.0.x stable --- actionpack/lib/action_controller.rb | 4 +- .../action_controller/vendor/rack-1.0.x/rack.rb | 90 ++++ .../vendor/rack-1.0.x/rack/adapter/camping.rb | 22 + .../rack-1.0.x/rack/auth/abstract/handler.rb | 37 ++ .../rack-1.0.x/rack/auth/abstract/request.rb | 37 ++ .../vendor/rack-1.0.x/rack/auth/basic.rb | 58 +++ .../vendor/rack-1.0.x/rack/auth/digest/md5.rb | 124 +++++ .../vendor/rack-1.0.x/rack/auth/digest/nonce.rb | 51 ++ .../vendor/rack-1.0.x/rack/auth/digest/params.rb | 55 ++ .../vendor/rack-1.0.x/rack/auth/digest/request.rb | 40 ++ .../vendor/rack-1.0.x/rack/auth/openid.rb | 487 ++++++++++++++++++ .../vendor/rack-1.0.x/rack/builder.rb | 63 +++ .../vendor/rack-1.0.x/rack/cascade.rb | 41 ++ .../vendor/rack-1.0.x/rack/chunked.rb | 49 ++ .../vendor/rack-1.0.x/rack/commonlogger.rb | 52 ++ .../vendor/rack-1.0.x/rack/conditionalget.rb | 47 ++ .../vendor/rack-1.0.x/rack/content_length.rb | 29 + .../vendor/rack-1.0.x/rack/content_type.rb | 23 + .../vendor/rack-1.0.x/rack/deflater.rb | 96 ++++ .../vendor/rack-1.0.x/rack/directory.rb | 153 ++++++ .../vendor/rack-1.0.x/rack/file.rb | 88 ++++ .../vendor/rack-1.0.x/rack/handler.rb | 69 +++ .../vendor/rack-1.0.x/rack/handler/cgi.rb | 61 +++ .../rack-1.0.x/rack/handler/evented_mongrel.rb | 8 + .../vendor/rack-1.0.x/rack/handler/fastcgi.rb | 88 ++++ .../vendor/rack-1.0.x/rack/handler/lsws.rb | 55 ++ .../vendor/rack-1.0.x/rack/handler/mongrel.rb | 84 +++ .../vendor/rack-1.0.x/rack/handler/scgi.rb | 59 +++ .../rack-1.0.x/rack/handler/swiftiplied_mongrel.rb | 8 + .../vendor/rack-1.0.x/rack/handler/thin.rb | 18 + .../vendor/rack-1.0.x/rack/handler/webrick.rb | 68 +++ .../vendor/rack-1.0.x/rack/head.rb | 19 + .../vendor/rack-1.0.x/rack/lint.rb | 537 ++++++++++++++++++++ .../vendor/rack-1.0.x/rack/lobster.rb | 65 +++ .../vendor/rack-1.0.x/rack/lock.rb | 16 + .../vendor/rack-1.0.x/rack/methodoverride.rb | 27 + .../vendor/rack-1.0.x/rack/mime.rb | 204 ++++++++ .../vendor/rack-1.0.x/rack/mock.rb | 184 +++++++ .../vendor/rack-1.0.x/rack/recursive.rb | 57 ++ .../vendor/rack-1.0.x/rack/reloader.rb | 106 ++++ .../vendor/rack-1.0.x/rack/request.rb | 248 +++++++++ .../vendor/rack-1.0.x/rack/response.rb | 183 +++++++ .../vendor/rack-1.0.x/rack/rewindable_input.rb | 98 ++++ .../vendor/rack-1.0.x/rack/session/abstract/id.rb | 142 +++++ .../vendor/rack-1.0.x/rack/session/cookie.rb | 91 ++++ .../vendor/rack-1.0.x/rack/session/memcache.rb | 109 ++++ .../vendor/rack-1.0.x/rack/session/pool.rb | 100 ++++ .../vendor/rack-1.0.x/rack/showexceptions.rb | 349 +++++++++++++ .../vendor/rack-1.0.x/rack/showstatus.rb | 106 ++++ .../vendor/rack-1.0.x/rack/static.rb | 38 ++ .../vendor/rack-1.0.x/rack/urlmap.rb | 55 ++ .../vendor/rack-1.0.x/rack/utils.rb | 520 +++++++++++++++++++ .../action_controller/vendor/rack-1.1.pre/rack.rb | 90 ---- .../vendor/rack-1.1.pre/rack/adapter/camping.rb | 22 - .../rack-1.1.pre/rack/auth/abstract/handler.rb | 37 -- .../rack-1.1.pre/rack/auth/abstract/request.rb | 37 -- .../vendor/rack-1.1.pre/rack/auth/basic.rb | 58 --- .../vendor/rack-1.1.pre/rack/auth/digest/md5.rb | 124 ----- .../vendor/rack-1.1.pre/rack/auth/digest/nonce.rb | 51 -- .../vendor/rack-1.1.pre/rack/auth/digest/params.rb | 55 -- .../rack-1.1.pre/rack/auth/digest/request.rb | 40 -- .../vendor/rack-1.1.pre/rack/auth/openid.rb | 480 ----------------- .../vendor/rack-1.1.pre/rack/builder.rb | 63 --- .../vendor/rack-1.1.pre/rack/cascade.rb | 36 -- .../vendor/rack-1.1.pre/rack/chunked.rb | 49 -- .../vendor/rack-1.1.pre/rack/commonlogger.rb | 61 --- .../vendor/rack-1.1.pre/rack/conditionalget.rb | 47 -- .../vendor/rack-1.1.pre/rack/content_length.rb | 29 - .../vendor/rack-1.1.pre/rack/content_type.rb | 23 - .../vendor/rack-1.1.pre/rack/deflater.rb | 96 ---- .../vendor/rack-1.1.pre/rack/directory.rb | 153 ------ .../vendor/rack-1.1.pre/rack/file.rb | 88 ---- .../vendor/rack-1.1.pre/rack/handler.rb | 69 --- .../vendor/rack-1.1.pre/rack/handler/cgi.rb | 61 --- .../rack-1.1.pre/rack/handler/evented_mongrel.rb | 8 - .../vendor/rack-1.1.pre/rack/handler/fastcgi.rb | 88 ---- .../vendor/rack-1.1.pre/rack/handler/lsws.rb | 55 -- .../vendor/rack-1.1.pre/rack/handler/mongrel.rb | 84 --- .../vendor/rack-1.1.pre/rack/handler/scgi.rb | 59 --- .../rack/handler/swiftiplied_mongrel.rb | 8 - .../vendor/rack-1.1.pre/rack/handler/thin.rb | 18 - .../vendor/rack-1.1.pre/rack/handler/webrick.rb | 67 --- .../vendor/rack-1.1.pre/rack/head.rb | 19 - .../vendor/rack-1.1.pre/rack/lint.rb | 537 -------------------- .../vendor/rack-1.1.pre/rack/lobster.rb | 65 --- .../vendor/rack-1.1.pre/rack/lock.rb | 16 - .../vendor/rack-1.1.pre/rack/methodoverride.rb | 27 - .../vendor/rack-1.1.pre/rack/mime.rb | 204 -------- .../vendor/rack-1.1.pre/rack/mock.rb | 184 ------- .../vendor/rack-1.1.pre/rack/recursive.rb | 57 -- .../vendor/rack-1.1.pre/rack/reloader.rb | 106 ---- .../vendor/rack-1.1.pre/rack/request.rb | 254 --------- .../vendor/rack-1.1.pre/rack/response.rb | 183 ------- .../vendor/rack-1.1.pre/rack/rewindable_input.rb | 98 ---- .../rack-1.1.pre/rack/session/abstract/id.rb | 142 ----- .../vendor/rack-1.1.pre/rack/session/cookie.rb | 91 ---- .../vendor/rack-1.1.pre/rack/session/memcache.rb | 109 ---- .../vendor/rack-1.1.pre/rack/session/pool.rb | 100 ---- .../vendor/rack-1.1.pre/rack/showexceptions.rb | 349 ------------- .../vendor/rack-1.1.pre/rack/showstatus.rb | 106 ---- .../vendor/rack-1.1.pre/rack/static.rb | 38 -- .../vendor/rack-1.1.pre/rack/urlmap.rb | 55 -- .../vendor/rack-1.1.pre/rack/utils.rb | 516 ------------------- 103 files changed, 5416 insertions(+), 5414 deletions(-) create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/adapter/camping.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/handler.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/request.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/basic.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/md5.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/nonce.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/params.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/request.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/openid.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/builder.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/cascade.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/chunked.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/commonlogger.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/conditionalget.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_length.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_type.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/deflater.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/directory.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/file.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/cgi.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/evented_mongrel.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/fastcgi.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/lsws.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/mongrel.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/scgi.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/swiftiplied_mongrel.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/thin.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/webrick.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/head.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lint.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lobster.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lock.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/methodoverride.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mime.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mock.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/recursive.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/reloader.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/request.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/response.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/rewindable_input.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/abstract/id.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/cookie.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/memcache.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/pool.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showexceptions.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showstatus.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/static.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/urlmap.rb create mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/utils.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/adapter/camping.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/request.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/basic.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/md5.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/params.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/request.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/openid.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/builder.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/cascade.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/chunked.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/commonlogger.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/conditionalget.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_length.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_type.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/deflater.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/directory.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/file.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/cgi.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/fastcgi.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/lsws.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/mongrel.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/scgi.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/thin.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/webrick.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/head.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lint.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lobster.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lock.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/methodoverride.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mime.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mock.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/recursive.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/reloader.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/request.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/response.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/rewindable_input.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/abstract/id.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/cookie.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/memcache.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/pool.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showexceptions.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showstatus.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/static.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/urlmap.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/utils.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 187c958..91f843e 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -32,10 +32,10 @@ rescue LoadError end begin - gem 'rack', '~> 1.1.0' + gem 'rack', '~> 1.0.0' require 'rack' rescue Gem::LoadError - require 'action_controller/vendor/rack-1.1.pre/rack' + require 'action_controller/vendor/rack-1.0.x/rack' end module ActionController diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack.rb new file mode 100644 index 0000000..371d015 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack.rb @@ -0,0 +1,90 @@ +# Copyright (C) 2007, 2008, 2009 Christian Neukirchen +# +# Rack is freely distributable under the terms of an MIT-style license. +# See COPYING or http://www.opensource.org/licenses/mit-license.php. + +path = File.expand_path(File.dirname(__FILE__)) +$:.unshift(path) unless $:.include?(path) + + +# The Rack main module, serving as a namespace for all core Rack +# modules and classes. +# +# All modules meant for use in your application are autoloaded here, +# so it should be enough just to require rack.rb in your code. + +module Rack + # The Rack protocol version number implemented. + VERSION = [1,0] + + # Return the Rack protocol version as a dotted string. + def self.version + VERSION.join(".") + end + + # Return the Rack release as a dotted string. + def self.release + "1.0" + end + + autoload :Builder, "rack/builder" + autoload :Cascade, "rack/cascade" + autoload :Chunked, "rack/chunked" + autoload :CommonLogger, "rack/commonlogger" + autoload :ConditionalGet, "rack/conditionalget" + autoload :ContentLength, "rack/content_length" + autoload :ContentType, "rack/content_type" + autoload :File, "rack/file" + autoload :Deflater, "rack/deflater" + autoload :Directory, "rack/directory" + autoload :ForwardRequest, "rack/recursive" + autoload :Handler, "rack/handler" + autoload :Head, "rack/head" + autoload :Lint, "rack/lint" + autoload :Lock, "rack/lock" + autoload :MethodOverride, "rack/methodoverride" + autoload :Mime, "rack/mime" + autoload :Recursive, "rack/recursive" + autoload :Reloader, "rack/reloader" + autoload :ShowExceptions, "rack/showexceptions" + autoload :ShowStatus, "rack/showstatus" + autoload :Static, "rack/static" + autoload :URLMap, "rack/urlmap" + autoload :Utils, "rack/utils" + + autoload :MockRequest, "rack/mock" + autoload :MockResponse, "rack/mock" + + autoload :Request, "rack/request" + autoload :Response, "rack/response" + + module Auth + autoload :Basic, "rack/auth/basic" + autoload :AbstractRequest, "rack/auth/abstract/request" + autoload :AbstractHandler, "rack/auth/abstract/handler" + autoload :OpenID, "rack/auth/openid" + module Digest + autoload :MD5, "rack/auth/digest/md5" + autoload :Nonce, "rack/auth/digest/nonce" + autoload :Params, "rack/auth/digest/params" + autoload :Request, "rack/auth/digest/request" + end + end + + module Session + autoload :Cookie, "rack/session/cookie" + autoload :Pool, "rack/session/pool" + autoload :Memcache, "rack/session/memcache" + end + + # *Adapters* connect Rack with third party web frameworks. + # + # Rack includes an adapter for Camping, see README for other + # frameworks supporting Rack in their code bases. + # + # Refer to the submodules for framework-specific calling details. + + module Adapter + autoload :Camping, "rack/adapter/camping" + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/adapter/camping.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/adapter/camping.rb new file mode 100644 index 0000000..63bc787 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/adapter/camping.rb @@ -0,0 +1,22 @@ +module Rack + module Adapter + class Camping + def initialize(app) + @app = app + end + + def call(env) + env["PATH_INFO"] ||= "" + env["SCRIPT_NAME"] ||= "" + controller = @app.run(env['rack.input'], env) + h = controller.headers + h.each_pair do |k,v| + if v.kind_of? URI + h[k] = v.to_s + end + end + [controller.status, controller.headers, [controller.body.to_s]] + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/handler.rb new file mode 100644 index 0000000..214df62 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/handler.rb @@ -0,0 +1,37 @@ +module Rack + module Auth + # Rack::Auth::AbstractHandler implements common authentication functionality. + # + # +realm+ should be set for all handlers. + + class AbstractHandler + + attr_accessor :realm + + def initialize(app, realm=nil, &authenticator) + @app, @realm, @authenticator = app, realm, authenticator + end + + + private + + def unauthorized(www_authenticate = challenge) + return [ 401, + { 'Content-Type' => 'text/plain', + 'Content-Length' => '0', + 'WWW-Authenticate' => www_authenticate.to_s }, + [] + ] + end + + def bad_request + return [ 400, + { 'Content-Type' => 'text/plain', + 'Content-Length' => '0' }, + [] + ] + end + + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/request.rb new file mode 100644 index 0000000..1d9ccec --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/request.rb @@ -0,0 +1,37 @@ +module Rack + module Auth + class AbstractRequest + + def initialize(env) + @env = env + end + + def provided? + !authorization_key.nil? + end + + def parts + @parts ||= @env[authorization_key].split(' ', 2) + end + + def scheme + @scheme ||= parts.first.downcase.to_sym + end + + def params + @params ||= parts.last + end + + + private + + AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION'] + + def authorization_key + @authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) } + end + + end + + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/basic.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/basic.rb new file mode 100644 index 0000000..9557224 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/basic.rb @@ -0,0 +1,58 @@ +require 'rack/auth/abstract/handler' +require 'rack/auth/abstract/request' + +module Rack + module Auth + # Rack::Auth::Basic implements HTTP Basic Authentication, as per RFC 2617. + # + # Initialize with the Rack application that you want protecting, + # and a block that checks if a username and password pair are valid. + # + # See also: example/protectedlobster.rb + + class Basic < AbstractHandler + + def call(env) + auth = Basic::Request.new(env) + + return unauthorized unless auth.provided? + + return bad_request unless auth.basic? + + if valid?(auth) + env['REMOTE_USER'] = auth.username + + return @app.call(env) + end + + unauthorized + end + + + private + + def challenge + 'Basic realm="%s"' % realm + end + + def valid?(auth) + @authenticator.call(*auth.credentials) + end + + class Request < Auth::AbstractRequest + def basic? + :basic == scheme + end + + def credentials + @credentials ||= params.unpack("m*").first.split(/:/, 2) + end + + def username + credentials.first + end + end + + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/md5.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/md5.rb new file mode 100644 index 0000000..e579dc9 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/md5.rb @@ -0,0 +1,124 @@ +require 'rack/auth/abstract/handler' +require 'rack/auth/digest/request' +require 'rack/auth/digest/params' +require 'rack/auth/digest/nonce' +require 'digest/md5' + +module Rack + module Auth + module Digest + # Rack::Auth::Digest::MD5 implements the MD5 algorithm version of + # HTTP Digest Authentication, as per RFC 2617. + # + # Initialize with the [Rack] application that you want protecting, + # and a block that looks up a plaintext password for a given username. + # + # +opaque+ needs to be set to a constant base64/hexadecimal string. + # + class MD5 < AbstractHandler + + attr_accessor :opaque + + attr_writer :passwords_hashed + + def initialize(*args) + super + @passwords_hashed = nil + end + + def passwords_hashed? + !!@passwords_hashed + end + + def call(env) + auth = Request.new(env) + + unless auth.provided? + return unauthorized + end + + if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth) + return bad_request + end + + if valid?(auth) + if auth.nonce.stale? + return unauthorized(challenge(:stale => true)) + else + env['REMOTE_USER'] = auth.username + + return @app.call(env) + end + end + + unauthorized + end + + + private + + QOP = 'auth'.freeze + + def params(hash = {}) + Params.new do |params| + params['realm'] = realm + params['nonce'] = Nonce.new.to_s + params['opaque'] = H(opaque) + params['qop'] = QOP + + hash.each { |k, v| params[k] = v } + end + end + + def challenge(hash = {}) + "Digest #{params(hash)}" + end + + def valid?(auth) + valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth) + end + + def valid_qop?(auth) + QOP == auth.qop + end + + def valid_opaque?(auth) + H(opaque) == auth.opaque + end + + def valid_nonce?(auth) + auth.nonce.valid? + end + + def valid_digest?(auth) + digest(auth, @authenticator.call(auth.username)) == auth.response + end + + def md5(data) + ::Digest::MD5.hexdigest(data) + end + + alias :H :md5 + + def KD(secret, data) + H([secret, data] * ':') + end + + def A1(auth, password) + [ auth.username, auth.realm, password ] * ':' + end + + def A2(auth) + [ auth.method, auth.uri ] * ':' + end + + def digest(auth, password) + password_hash = passwords_hashed? ? password : H(A1(auth, password)) + + KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, QOP, H(A2(auth)) ] * ':') + end + + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/nonce.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/nonce.rb new file mode 100644 index 0000000..dbe109f --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/nonce.rb @@ -0,0 +1,51 @@ +require 'digest/md5' + +module Rack + module Auth + module Digest + # Rack::Auth::Digest::Nonce is the default nonce generator for the + # Rack::Auth::Digest::MD5 authentication handler. + # + # +private_key+ needs to set to a constant string. + # + # +time_limit+ can be optionally set to an integer (number of seconds), + # to limit the validity of the generated nonces. + + class Nonce + + class << self + attr_accessor :private_key, :time_limit + end + + def self.parse(string) + new(*string.unpack("m*").first.split(' ', 2)) + end + + def initialize(timestamp = Time.now, given_digest = nil) + @timestamp, @given_digest = timestamp.to_i, given_digest + end + + def to_s + [([ @timestamp, digest ] * ' ')].pack("m*").strip + end + + def digest + ::Digest::MD5.hexdigest([ @timestamp, self.class.private_key ] * ':') + end + + def valid? + digest == @given_digest + end + + def stale? + !self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit + end + + def fresh? + !stale? + end + + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/params.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/params.rb new file mode 100644 index 0000000..730e2ef --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/params.rb @@ -0,0 +1,55 @@ +module Rack + module Auth + module Digest + class Params < Hash + + def self.parse(str) + split_header_value(str).inject(new) do |header, param| + k, v = param.split('=', 2) + header[k] = dequote(v) + header + end + end + + def self.dequote(str) # From WEBrick::HTTPUtils + ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup + ret.gsub!(/\\(.)/, "\\1") + ret + end + + def self.split_header_value(str) + str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] } + end + + def initialize + super + + yield self if block_given? + end + + def [](k) + super k.to_s + end + + def []=(k, v) + super k.to_s, v.to_s + end + + UNQUOTED = ['qop', 'nc', 'stale'] + + def to_s + inject([]) do |parts, (k, v)| + parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v)) + parts + end.join(', ') + end + + def quote(str) # From WEBrick::HTTPUtils + '"' << str.gsub(/[\\\"]/o, "\\\1") << '"' + end + + end + end + end +end + diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/request.rb new file mode 100644 index 0000000..a8aa3bf --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/request.rb @@ -0,0 +1,40 @@ +require 'rack/auth/abstract/request' +require 'rack/auth/digest/params' +require 'rack/auth/digest/nonce' + +module Rack + module Auth + module Digest + class Request < Auth::AbstractRequest + + def method + @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD'] + end + + def digest? + :digest == scheme + end + + def correct_uri? + (@env['SCRIPT_NAME'].to_s + @env['PATH_INFO'].to_s) == uri + end + + def nonce + @nonce ||= Nonce.parse(params['nonce']) + end + + def params + @params ||= Params.parse(parts.last) + end + + def method_missing(sym) + if params.has_key? key = sym.to_s + return params[key] + end + super + end + + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/openid.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/openid.rb new file mode 100644 index 0000000..43cbe4f --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/openid.rb @@ -0,0 +1,487 @@ +# AUTHOR: Scytrin dai Kinthra ; blink#ruby-lang@irc.freenode.net + +gem 'ruby-openid', '~> 2' if defined? Gem +require 'rack/request' +require 'rack/utils' +require 'rack/auth/abstract/handler' + +require 'uri' +require 'openid' +require 'openid/extension' +require 'openid/store/memory' + +module Rack + class Request + def openid_request + @env['rack.auth.openid.request'] + end + + def openid_response + @env['rack.auth.openid.response'] + end + end + + module Auth + + # Rack::Auth::OpenID provides a simple method for setting up an OpenID + # Consumer. It requires the ruby-openid library from janrain to operate, + # as well as a rack method of session management. + # + # The ruby-openid home page is at http://openidenabled.com/ruby-openid/. + # + # The OpenID specifications can be found at + # http://openid.net/specs/openid-authentication-1_1.html + # and + # http://openid.net/specs/openid-authentication-2_0.html. Documentation + # for published OpenID extensions and related topics can be found at + # http://openid.net/developers/specs/. + # + # It is recommended to read through the OpenID spec, as well as + # ruby-openid's documentation, to understand what exactly goes on. However + # a setup as simple as the presented examples is enough to provide + # Consumer functionality. + # + # This library strongly intends to utilize the OpenID 2.0 features of the + # ruby-openid library, which provides OpenID 1.0 compatiblity. + # + # NOTE: Due to the amount of data that this library stores in the + # session, Rack::Session::Cookie may fault. + # + # == Examples + # + # simple_oid = OpenID.new('http://mysite.com/') + # + # return_oid = OpenID.new('http://mysite.com/', { + # :return_to => 'http://mysite.com/openid' + # }) + # + # complex_oid = OpenID.new('http://mysite.com/', + # :immediate => true, + # :extensions => { + # ::OpenID::SReg => [['email'],['nickname']] + # } + # ) + # + # = Advanced + # + # Most of the functionality of this library is encapsulated such that + # expansion and overriding functions isn't difficult nor tricky. + # Alternately, to avoid opening up singleton objects or subclassing, a + # wrapper rack middleware can be composed to act upon Auth::OpenID's + # responses. See #check and #finish for locations of pertinent data. + # + # == Responses + # + # To change the responses that Auth::OpenID returns, override the methods + # #redirect, #bad_request, #unauthorized, #access_denied, and + # #foreign_server_failure. + # + # Additionally #confirm_post_params is used when the URI would exceed + # length limits on a GET request when doing the initial verification + # request. + # + # == Processing + # + # To change methods of processing completed transactions, override the + # methods #success, #setup_needed, #cancel, and #failure. Please ensure + # the returned object is a rack compatible response. + # + # The first argument is an OpenID::Response, the second is a + # Rack::Request of the current request, the last is the hash used in + # ruby-openid handling, which can be found manually at + # env['rack.session'][:openid]. + # + # This is useful if you wanted to expand the processing done, such as + # setting up user accounts. + # + # oid_app = Rack::Auth::OpenID.new realm, :return_to => return_to + # def oid_app.success oid, request, session + # user = Models::User[oid.identity_url] + # user ||= Models::User.create_from_openid oid + # request['rack.session'][:user] = user.id + # redirect MyApp.site_home + # end + # + # site_map['/openid'] = oid_app + # map = Rack::URLMap.new site_map + # ... + + class OpenID + # Raised if an incompatible session is being used. + class NoSession < RuntimeError; end + # Raised if an extension not matching specifications is provided. + class BadExtension < RuntimeError; end + # Possible statuses returned from consumer responses. See definitions + # in the ruby-openid library. + ValidStatus = [ + ::OpenID::Consumer::SUCCESS, + ::OpenID::Consumer::FAILURE, + ::OpenID::Consumer::CANCEL, + ::OpenID::Consumer::SETUP_NEEDED + ] + + # The first argument is the realm, identifying the site they are trusting + # with their identity. This is required, also treated as the trust_root + # in OpenID 1.x exchanges. + # + # The lits of acceptable options include :return_to, :session_key, + # :openid_param, :store, :immediate, :extensions. + # + # :return_to defines the url to return to after the client + # authenticates with the openid service provider. This url should point + # to where Rack::Auth::OpenID is mounted. If unprovided, the url of + # the current request is used. + # + # :session_key defines the key to the session hash in the env. + # The default is 'rack.session'. + # + # :openid_param defines at what key in the request parameters to + # find the identifier to resolve. As per the 2.0 spec, the default is + # 'openid_identifier'. + # + # :store defined what OpenID Store to use for persistant + # information. By default a Store::Memory is used. + # + # :immediate as true will make initial requests to be of an + # immediate type. This is false by default. See OpenID specification + # documentation. + # + # :extensions should be a hash of openid extension + # implementations. The key should be the extension module, the value + # should be an array of arguments for extension::Request.new(). + # The hash is iterated over and passed to #add_extension for processing. + # Please see #add_extension for further documentation. + + def initialize(realm, options={}) + realm = URI(realm) + raise ArgumentError, "Invalid realm: #{realm}" \ + unless realm.absolute? \ + and realm.fragment.nil? \ + and realm.scheme =~ /^https?$/ \ + and realm.host =~ /^(\*\.)?#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+/ + realm.path = '/' if realm.path.empty? + @realm = realm.to_s + + if ruri = options[:return_to] + ruri = URI(ruri) + raise ArgumentError, "Invalid return_to: #{ruri}" \ + unless ruri.absolute? \ + and ruri.scheme =~ /^https?$/ \ + and ruri.fragment.nil? + raise ArgumentError, "return_to #{ruri} not within realm #{realm}" \ + unless self.within_realm?(ruri) + @return_to = ruri.to_s + end + + @session_key = options[:session_key] || 'rack.session' + @openid_param = options[:openid_param] || 'openid_identifier' + @store = options[:store] || ::OpenID::Store::Memory.new + @immediate = !!options[:immediate] + + @extensions = {} + if extensions = options[:extensions] + extensions.each do |ext, args| + add_extension(ext, *args) + end + end + + # Undocumented, semi-experimental + @anonymous = !!options[:anonymous] + end + + attr_reader :realm, :return_to, :session_key, :openid_param, :store, + :immediate, :extensions + + # Sets up and uses session data at :openid within the session. + # Errors in this setup will raise a NoSession exception. + # + # If the parameter 'openid.mode' is set, which implies a followup from + # the openid server, processing is passed to #finish and the result is + # returned. However, if there is no appropriate openid information in the + # session, a 400 error is returned. + # + # If the parameter specified by options[:openid_param] is + # present, processing is passed to #check and the result is returned. + # + # If neither of these conditions are met, #bad_request is called. + + def call(env) + env['rack.auth.openid'] = self + env_session = env[@session_key] + unless env_session and env_session.is_a?(Hash) + raise NoSession, 'No compatible session.' + end + # let us work in our own namespace... + session = (env_session[:openid] ||= {}) + unless session and session.is_a?(Hash) + raise NoSession, 'Incompatible openid session.' + end + + request = Rack::Request.new(env) + consumer = ::OpenID::Consumer.new(session, @store) + + if mode = request.GET['openid.mode'] + finish(consumer, session, request) + elsif request.GET[@openid_param] + check(consumer, session, request) + else + bad_request + end + end + + # As the first part of OpenID consumer action, #check retrieves the data + # required for completion. + # + # If all parameters fit within the max length of a URI, a 303 redirect + # will be returned. Otherwise #confirm_post_params will be called. + # + # Any messages from OpenID's request are logged to env['rack.errors'] + # + # env['rack.auth.openid.request'] is the openid checkid request + # instance. + # + # session[:openid_param] is set to the openid identifier + # provided by the user. + # + # session[:return_to] is set to the return_to uri given to the + # identity provider. + + def check(consumer, session, req) + oid = consumer.begin(req.GET[@openid_param], @anonymous) + req.env['rack.auth.openid.request'] = oid + req.env['rack.errors'].puts(oid.message) + p oid if $DEBUG + + ## Extension support + extensions.each do |ext,args| + oid.add_extension(ext::Request.new(*args)) + end + + session[:openid_param] = req.GET[openid_param] + return_to_uri = return_to ? return_to : req.url + session[:return_to] = return_to_uri + immediate = session.key?(:setup_needed) ? false : immediate + + if oid.send_redirect?(realm, return_to_uri, immediate) + redirect(oid.redirect_url(realm, return_to_uri, immediate)) + else + confirm_post_params(oid, realm, return_to_uri, immediate) + end + rescue ::OpenID::DiscoveryFailure => e + # thrown from inside OpenID::Consumer#begin by yadis stuff + req.env['rack.errors'].puts( [e.message, *e.backtrace]*"\n" ) + return foreign_server_failure + end + + # This is the final portion of authentication. + # If successful, a redirect to the realm is be returned. + # Data gathered from extensions are stored in session[:openid] with the + # extension's namespace uri as the key. + # + # Any messages from OpenID's response are logged to env['rack.errors'] + # + # env['rack.auth.openid.response'] will contain the openid + # response. + + def finish(consumer, session, req) + oid = consumer.complete(req.GET, req.url) + req.env['rack.auth.openid.response'] = oid + req.env['rack.errors'].puts(oid.message) + p oid if $DEBUG + + if ValidStatus.include?(oid.status) + __send__(oid.status, oid, req, session) + else + invalid_status(oid, req, session) + end + end + + # The first argument should be the main extension module. + # The extension module should contain the constants: + # * class Request, should have OpenID::Extension as an ancestor + # * class Response, should have OpenID::Extension as an ancestor + # * string NS_URI, which defining the namespace of the extension + # + # All trailing arguments will be passed to extension::Request.new in + # #check. + # The openid response will be passed to + # extension::Response#from_success_response, oid#get_extension_args will + # be called on the result to attain the gathered data. + # + # This method returns the key at which the response data will be found in + # the session, which is the namespace uri by default. + + def add_extension(ext, *args) + raise BadExtension unless valid_extension?(ext) + extensions[ext] = args + return ext::NS_URI + end + + # Checks the validitity, in the context of usage, of a submitted + # extension. + + def valid_extension?(ext) + if not %w[NS_URI Request Response].all?{|c| ext.const_defined?(c) } + raise ArgumentError, 'Extension is missing constants.' + elsif not ext::Response.respond_to?(:from_success_response) + raise ArgumentError, 'Response is missing required method.' + end + return true + rescue + return false + end + + # Checks the provided uri to ensure it'd be considered within the realm. + # is currently not compatible with wildcard realms. + + def within_realm? uri + uri = URI.parse(uri.to_s) + realm = URI.parse(self.realm) + return false unless uri.absolute? + return false unless uri.path[0, realm.path.size] == realm.path + return false unless uri.host == realm.host or realm.host[/^\*\./] + # for wildcard support, is awkward with URI limitations + realm_match = Regexp.escape(realm.host). + sub(/^\*\./,"^#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+.")+'$' + return false unless uri.host.match(realm_match) + return true + end + + alias_method :include?, :within_realm? + + protected + + # Returns an html form page for posting to an Identity Provider if the + # GET request would exceed the upper URI length limit. + + def confirm_post_params(oid, realm, return_to, immediate) + response = Rack::Response.new ''+ + 'Confirm...'+ + ''+oid.form_markup(realm, return_to, immediate)+''+ + '' + response.finish + end + + # Returns a 303 redirect with the destination of that provided by the + # argument. + + def redirect(uri) + [ 303, {'Content-Type'=>'text/plain', 'Content-Length'=>'0', + 'Location' => uri}, + [] ] + end + + # Returns an empty 400 response. + + def bad_request + [ 400, {'Content-Type'=>'text/plain', 'Content-Length'=>'0'}, + [''] ] + end + + # Returns a basic unauthorized 401 response. + + def unauthorized + [ 401, {'Content-Type' => 'text/plain', 'Content-Length' => '13'}, + ['Unauthorized.'] ] + end + + # Returns a basic access denied 403 response. + + def access_denied + [ 403, {'Content-Type' => 'text/plain', 'Content-Length' => '14'}, + ['Access denied.'] ] + end + + # Returns a 503 response to be used if communication with the remote + # OpenID server fails. + + def foreign_server_failure + [ 503, {'Content-Type'=>'text/plain', 'Content-Length' => '23'}, + ['Foreign server failure.'] ] + end + + private + + # Called to complete processing on a successful transaction. + # Within the openid session, :openid_identity and :openid_identifier are + # set to the user friendly and the standard representation of the + # validated identity. All other data in the openid session is cleared. + + def success(oid, request, session) + session.clear + session[:openid_identity] = oid.display_identifier + session[:openid_identifier] = oid.identity_url + extensions.keys.each do |ext| + label = ext.name[/[^:]+$/].downcase + response = ext::Response.from_success_response(oid) + session[label] = response.data + end + redirect(realm) + end + + # Called if the Identity Provider indicates further setup by the user is + # required. + # The identifier is retrived from the openid session at :openid_param. + # And :setup_needed is set to true to prevent looping. + + def setup_needed(oid, request, session) + identifier = session[:openid_param] + session[:setup_needed] = true + redirect(req.script_name + '?' + openid_param + '=' + identifier) + end + + # Called if the user indicates they wish to cancel identification. + # Data within openid session is cleared. + + def cancel(oid, request, session) + session.clear + access_denied + end + + # Called if the Identity Provider indicates the user is unable to confirm + # their identity. Data within the openid session is left alone, in case + # of swarm auth attacks. + + def failure(oid, request, session) + unauthorized + end + + # To be called if there is no method for handling the OpenID response + # status. + + def invalid_status(oid, request, session) + msg = 'Invalid status returned by the OpenID authorization reponse.' + [ 500, + {'Content-Type'=>'text/plain','Content-Length'=>msg.length.to_s}, + [msg] ] + end + end + + # A class developed out of the request to use OpenID as an authentication + # middleware. The request will be sent to the OpenID instance unless the + # block evaluates to true. For example in rackup, you can use it as such: + # + # use Rack::Session::Pool + # use Rack::Auth::OpenIDAuth, realm, openid_options do |env| + # env['rack.session'][:authkey] == a_string + # end + # run RackApp + # + # Or simply: + # + # app = Rack::Auth::OpenIDAuth.new app, realm, openid_options, &auth + + class OpenIDAuth < Rack::Auth::AbstractHandler + attr_reader :oid + def initialize(app, realm, options={}, &auth) + @oid = OpenID.new(realm, options) + super(app, &auth) + end + + def call(env) + to = @authenticator.call(env) ? @app : @oid + to.call(env) + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/builder.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/builder.rb new file mode 100644 index 0000000..295235e --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/builder.rb @@ -0,0 +1,63 @@ +module Rack + # Rack::Builder implements a small DSL to iteratively construct Rack + # applications. + # + # Example: + # + # app = Rack::Builder.new { + # use Rack::CommonLogger + # use Rack::ShowExceptions + # map "/lobster" do + # use Rack::Lint + # run Rack::Lobster.new + # end + # } + # + # Or + # + # app = Rack::Builder.app do + # use Rack::CommonLogger + # lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] } + # end + # + # +use+ adds a middleware to the stack, +run+ dispatches to an application. + # You can use +map+ to construct a Rack::URLMap in a convenient way. + + class Builder + def initialize(&block) + @ins = [] + instance_eval(&block) if block_given? + end + + def self.app(&block) + self.new(&block).to_app + end + + def use(middleware, *args, &block) + @ins << lambda { |app| middleware.new(app, *args, &block) } + end + + def run(app) + @ins << app #lambda { |nothing| app } + end + + def map(path, &block) + if @ins.last.kind_of? Hash + @ins.last[path] = self.class.new(&block).to_app + else + @ins << {} + map(path, &block) + end + end + + def to_app + @ins[-1] = Rack::URLMap.new(@ins.last) if Hash === @ins.last + inner_app = @ins.last + @ins[0...-1].reverse.inject(inner_app) { |a, e| e.call(a) } + end + + def call(env) + to_app.call(env) + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/cascade.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/cascade.rb new file mode 100644 index 0000000..14c3e54 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/cascade.rb @@ -0,0 +1,41 @@ +module Rack + # Rack::Cascade tries an request on several apps, and returns the + # first response that is not 404 (or in a list of configurable + # status codes). + + class Cascade + NotFound = [404, {}, []] + + attr_reader :apps + + def initialize(apps, catch=404) + @apps = []; @has_app = {} + apps.each { |app| add app } + + @catch = {} + [*catch].each { |status| @catch[status] = true } + end + + def call(env) + result = NotFound + + @apps.each do |app| + result = app.call(env) + break unless @catch.include?(result[0].to_i) + end + + result + end + + def add app + @has_app[app] = true + @apps << app + end + + def include? app + @has_app.include? app + end + + alias_method :<<, :add + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/chunked.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/chunked.rb new file mode 100644 index 0000000..280d89d --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/chunked.rb @@ -0,0 +1,49 @@ +require 'rack/utils' + +module Rack + + # Middleware that applies chunked transfer encoding to response bodies + # when the response does not include a Content-Length header. + class Chunked + include Rack::Utils + + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + headers = HeaderHash.new(headers) + + if env['HTTP_VERSION'] == 'HTTP/1.0' || + STATUS_WITH_NO_ENTITY_BODY.include?(status) || + headers['Content-Length'] || + headers['Transfer-Encoding'] + [status, headers.to_hash, body] + else + dup.chunk(status, headers, body) + end + end + + def chunk(status, headers, body) + @body = body + headers.delete('Content-Length') + headers['Transfer-Encoding'] = 'chunked' + [status, headers.to_hash, self] + end + + def each + term = "\r\n" + @body.each do |chunk| + size = bytesize(chunk) + next if size == 0 + yield [size.to_s(16), term, chunk, term].join + end + yield ["0", term, "", term].join + end + + def close + @body.close if @body.respond_to?(:close) + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/commonlogger.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/commonlogger.rb new file mode 100644 index 0000000..880f0fb --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/commonlogger.rb @@ -0,0 +1,52 @@ +module Rack + # Rack::CommonLogger forwards every request to an +app+ given, and + # logs a line in the Apache common log format to the +logger+, or + # rack.errors by default. + class CommonLogger + # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common + # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 - + # %{%s - %s [%s] "%s %s%s %s" %d %s\n} % + FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} + + def initialize(app, logger=nil) + @app = app + @logger = logger + end + + def call(env) + began_at = Time.now + status, header, body = @app.call(env) + log(env, status, header, began_at) + [status, header, body] + end + + private + + def log(env, status, header, began_at) + now = Time.now + length = extract_content_length(header) + + logger = @logger || env['rack.errors'] + logger.write FORMAT % [ + env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-", + env["REMOTE_USER"] || "-", + now.strftime("%d/%b/%Y %H:%M:%S"), + env["REQUEST_METHOD"], + env["PATH_INFO"], + env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"], + env["HTTP_VERSION"], + status.to_s[0..3], + length, + now - began_at ] + end + + def extract_content_length(headers) + headers.each do |key, value| + if key.downcase == 'content-length' + return value.to_s == '0' ? '-' : value + end + end + '-' + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/conditionalget.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/conditionalget.rb new file mode 100644 index 0000000..046ebdb --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/conditionalget.rb @@ -0,0 +1,47 @@ +require 'rack/utils' + +module Rack + + # Middleware that enables conditional GET using If-None-Match and + # If-Modified-Since. The application should set either or both of the + # Last-Modified or Etag response headers according to RFC 2616. When + # either of the conditions is met, the response body is set to be zero + # length and the response status is set to 304 Not Modified. + # + # Applications that defer response body generation until the body's each + # message is received will avoid response body generation completely when + # a conditional GET matches. + # + # Adapted from Michael Klishin's Merb implementation: + # http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb + class ConditionalGet + def initialize(app) + @app = app + end + + def call(env) + return @app.call(env) unless %w[GET HEAD].include?(env['REQUEST_METHOD']) + + status, headers, body = @app.call(env) + headers = Utils::HeaderHash.new(headers) + if etag_matches?(env, headers) || modified_since?(env, headers) + status = 304 + headers.delete('Content-Type') + headers.delete('Content-Length') + body = [] + end + [status, headers, body] + end + + private + def etag_matches?(env, headers) + etag = headers['Etag'] and etag == env['HTTP_IF_NONE_MATCH'] + end + + def modified_since?(env, headers) + last_modified = headers['Last-Modified'] and + last_modified == env['HTTP_IF_MODIFIED_SINCE'] + end + end + +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_length.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_length.rb new file mode 100644 index 0000000..1e56d43 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_length.rb @@ -0,0 +1,29 @@ +require 'rack/utils' + +module Rack + # Sets the Content-Length header on responses with fixed-length bodies. + class ContentLength + include Rack::Utils + + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + headers = HeaderHash.new(headers) + + if !STATUS_WITH_NO_ENTITY_BODY.include?(status) && + !headers['Content-Length'] && + !headers['Transfer-Encoding'] && + (body.respond_to?(:to_ary) || body.respond_to?(:to_str)) + + body = [body] if body.respond_to?(:to_str) # rack 0.4 compat + length = body.to_ary.inject(0) { |len, part| len + bytesize(part) } + headers['Content-Length'] = length.to_s + end + + [status, headers, body] + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_type.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_type.rb new file mode 100644 index 0000000..0c1e1ca --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_type.rb @@ -0,0 +1,23 @@ +require 'rack/utils' + +module Rack + + # Sets the Content-Type header on responses which don't have one. + # + # Builder Usage: + # use Rack::ContentType, "text/plain" + # + # When no content type argument is provided, "text/html" is assumed. + class ContentType + def initialize(app, content_type = "text/html") + @app, @content_type = app, content_type + end + + def call(env) + status, headers, body = @app.call(env) + headers = Utils::HeaderHash.new(headers) + headers['Content-Type'] ||= @content_type + [status, headers.to_hash, body] + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/deflater.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/deflater.rb new file mode 100644 index 0000000..14137a9 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/deflater.rb @@ -0,0 +1,96 @@ +require "zlib" +require "stringio" +require "time" # for Time.httpdate +require 'rack/utils' + +module Rack + class Deflater + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + headers = Utils::HeaderHash.new(headers) + + # Skip compressing empty entity body responses and responses with + # no-transform set. + if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) || + headers['Cache-Control'].to_s =~ /\bno-transform\b/ + return [status, headers, body] + end + + request = Request.new(env) + + encoding = Utils.select_best_encoding(%w(gzip deflate identity), + request.accept_encoding) + + # Set the Vary HTTP header. + vary = headers["Vary"].to_s.split(",").map { |v| v.strip } + unless vary.include?("*") || vary.include?("Accept-Encoding") + headers["Vary"] = vary.push("Accept-Encoding").join(",") + end + + case encoding + when "gzip" + headers['Content-Encoding'] = "gzip" + headers.delete('Content-Length') + mtime = headers.key?("Last-Modified") ? + Time.httpdate(headers["Last-Modified"]) : Time.now + [status, headers, GzipStream.new(body, mtime)] + when "deflate" + headers['Content-Encoding'] = "deflate" + headers.delete('Content-Length') + [status, headers, DeflateStream.new(body)] + when "identity" + [status, headers, body] + when nil + message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found." + [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]] + end + end + + class GzipStream + def initialize(body, mtime) + @body = body + @mtime = mtime + end + + def each(&block) + @writer = block + gzip =::Zlib::GzipWriter.new(self) + gzip.mtime = @mtime + @body.each { |part| gzip << part } + @body.close if @body.respond_to?(:close) + gzip.close + @writer = nil + end + + def write(data) + @writer.call(data) + end + end + + class DeflateStream + DEFLATE_ARGS = [ + Zlib::DEFAULT_COMPRESSION, + # drop the zlib header which causes both Safari and IE to choke + -Zlib::MAX_WBITS, + Zlib::DEF_MEM_LEVEL, + Zlib::DEFAULT_STRATEGY + ] + + def initialize(body) + @body = body + end + + def each + deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS) + @body.each { |part| yield deflater.deflate(part) } + @body.close if @body.respond_to?(:close) + yield deflater.finish + nil + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/directory.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/directory.rb new file mode 100644 index 0000000..acdd302 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/directory.rb @@ -0,0 +1,153 @@ +require 'time' +require 'rack/utils' +require 'rack/mime' + +module Rack + # Rack::Directory serves entries below the +root+ given, according to the + # path info of the Rack request. If a directory is found, the file's contents + # will be presented in an html based index. If a file is found, the env will + # be passed to the specified +app+. + # + # If +app+ is not specified, a Rack::File of the same +root+ will be used. + + class Directory + DIR_FILE = "%s%s%s%s" + DIR_PAGE = <<-PAGE + + %s + + + +

%s

+
+ + + + + + + +%s +
NameSizeTypeLast Modified
+
+ + PAGE + + attr_reader :files + attr_accessor :root, :path + + def initialize(root, app=nil) + @root = F.expand_path(root) + @app = app || Rack::File.new(@root) + end + + def call(env) + dup._call(env) + end + + F = ::File + + def _call(env) + @env = env + @script_name = env['SCRIPT_NAME'] + @path_info = Utils.unescape(env['PATH_INFO']) + + if forbidden = check_forbidden + forbidden + else + @path = F.join(@root, @path_info) + list_path + end + end + + def check_forbidden + return unless @path_info.include? ".." + + body = "Forbidden\n" + size = Rack::Utils.bytesize(body) + return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]] + end + + def list_directory + @files = [['../','Parent Directory','','','']] + glob = F.join(@path, '*') + + Dir[glob].sort.each do |node| + stat = stat(node) + next unless stat + basename = F.basename(node) + ext = F.extname(node) + + url = F.join(@script_name, @path_info, basename) + size = stat.size + type = stat.directory? ? 'directory' : Mime.mime_type(ext) + size = stat.directory? ? '-' : filesize_format(size) + mtime = stat.mtime.httpdate + url << '/' if stat.directory? + basename << '/' if stat.directory? + + @files << [ url, basename, size, type, mtime ] + end + + return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ] + end + + def stat(node, max = 10) + F.stat(node) + rescue Errno::ENOENT, Errno::ELOOP + return nil + end + + # TODO: add correct response if not readable, not sure if 404 is the best + # option + def list_path + @stat = F.stat(@path) + + if @stat.readable? + return @app.call(@env) if @stat.file? + return list_directory if @stat.directory? + else + raise Errno::ENOENT, 'No such file or directory' + end + + rescue Errno::ENOENT, Errno::ELOOP + return entity_not_found + end + + def entity_not_found + body = "Entity not found: #{@path_info}\n" + size = Rack::Utils.bytesize(body) + return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]] + end + + def each + show_path = @path.sub(/^#{@root}/,'') + files = @files.map{|f| DIR_FILE % f }*"\n" + page = DIR_PAGE % [ show_path, show_path , files ] + page.each_line{|l| yield l } + end + + # Stolen from Ramaze + + FILESIZE_FORMAT = [ + ['%.1fT', 1 << 40], + ['%.1fG', 1 << 30], + ['%.1fM', 1 << 20], + ['%.1fK', 1 << 10], + ] + + def filesize_format(int) + FILESIZE_FORMAT.each do |format, size| + return format % (int.to_f / size) if int >= size + end + + int.to_s + 'B' + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/file.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/file.rb new file mode 100644 index 0000000..fe62bd6 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/file.rb @@ -0,0 +1,88 @@ +require 'time' +require 'rack/utils' +require 'rack/mime' + +module Rack + # Rack::File serves files below the +root+ given, according to the + # path info of the Rack request. + # + # Handlers can detect if bodies are a Rack::File, and use mechanisms + # like sendfile on the +path+. + + class File + attr_accessor :root + attr_accessor :path + + alias :to_path :path + + def initialize(root) + @root = root + end + + def call(env) + dup._call(env) + end + + F = ::File + + def _call(env) + @path_info = Utils.unescape(env["PATH_INFO"]) + return forbidden if @path_info.include? ".." + + @path = F.join(@root, @path_info) + + begin + if F.file?(@path) && F.readable?(@path) + serving + else + raise Errno::EPERM + end + rescue SystemCallError + not_found + end + end + + def forbidden + body = "Forbidden\n" + [403, {"Content-Type" => "text/plain", + "Content-Length" => body.size.to_s}, + [body]] + end + + # NOTE: + # We check via File::size? whether this file provides size info + # via stat (e.g. /proc files often don't), otherwise we have to + # figure it out by reading the whole file into memory. And while + # we're at it we also use this as body then. + + def serving + if size = F.size?(@path) + body = self + else + body = [F.read(@path)] + size = Utils.bytesize(body.first) + end + + [200, { + "Last-Modified" => F.mtime(@path).httpdate, + "Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain'), + "Content-Length" => size.to_s + }, body] + end + + def not_found + body = "File not found: #{@path_info}\n" + [404, {"Content-Type" => "text/plain", + "Content-Length" => body.size.to_s}, + [body]] + end + + def each + F.open(@path, "rb") { |file| + while part = file.read(8192) + yield part + end + } + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler.rb new file mode 100644 index 0000000..5624a1e --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler.rb @@ -0,0 +1,69 @@ +module Rack + # *Handlers* connect web servers with Rack. + # + # Rack includes Handlers for Mongrel, WEBrick, FastCGI, CGI, SCGI + # and LiteSpeed. + # + # Handlers usually are activated by calling MyHandler.run(myapp). + # A second optional hash can be passed to include server-specific + # configuration. + module Handler + def self.get(server) + return unless server + server = server.to_s + + if klass = @handlers[server] + obj = Object + klass.split("::").each { |x| obj = obj.const_get(x) } + obj + else + try_require('rack/handler', server) + const_get(server) + end + end + + # Transforms server-name constants to their canonical form as filenames, + # then tries to require them but silences the LoadError if not found + # + # Naming convention: + # + # Foo # => 'foo' + # FooBar # => 'foo_bar.rb' + # FooBAR # => 'foobar.rb' + # FOObar # => 'foobar.rb' + # FOOBAR # => 'foobar.rb' + # FooBarBaz # => 'foo_bar_baz.rb' + def self.try_require(prefix, const_name) + file = const_name.gsub(/^[A-Z]+/) { |pre| pre.downcase }. + gsub(/[A-Z]+[^A-Z]/, '_\&').downcase + + require(::File.join(prefix, file)) + rescue LoadError + end + + def self.register(server, klass) + @handlers ||= {} + @handlers[server] = klass + end + + autoload :CGI, "rack/handler/cgi" + autoload :FastCGI, "rack/handler/fastcgi" + autoload :Mongrel, "rack/handler/mongrel" + autoload :EventedMongrel, "rack/handler/evented_mongrel" + autoload :SwiftipliedMongrel, "rack/handler/swiftiplied_mongrel" + autoload :WEBrick, "rack/handler/webrick" + autoload :LSWS, "rack/handler/lsws" + autoload :SCGI, "rack/handler/scgi" + autoload :Thin, "rack/handler/thin" + + register 'cgi', 'Rack::Handler::CGI' + register 'fastcgi', 'Rack::Handler::FastCGI' + register 'mongrel', 'Rack::Handler::Mongrel' + register 'emongrel', 'Rack::Handler::EventedMongrel' + register 'smongrel', 'Rack::Handler::SwiftipliedMongrel' + register 'webrick', 'Rack::Handler::WEBrick' + register 'lsws', 'Rack::Handler::LSWS' + register 'scgi', 'Rack::Handler::SCGI' + register 'thin', 'Rack::Handler::Thin' + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/cgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/cgi.rb new file mode 100644 index 0000000..f45f3d7 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/cgi.rb @@ -0,0 +1,61 @@ +require 'rack/content_length' + +module Rack + module Handler + class CGI + def self.run(app, options=nil) + serve app + end + + def self.serve(app) + app = ContentLength.new(app) + + env = ENV.to_hash + env.delete "HTTP_CONTENT_LENGTH" + + env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" + + env.update({"rack.version" => [1,0], + "rack.input" => $stdin, + "rack.errors" => $stderr, + + "rack.multithread" => false, + "rack.multiprocess" => true, + "rack.run_once" => true, + + "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" + }) + + env["QUERY_STRING"] ||= "" + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["REQUEST_PATH"] ||= "/" + + status, headers, body = app.call(env) + begin + send_headers status, headers + send_body body + ensure + body.close if body.respond_to? :close + end + end + + def self.send_headers(status, headers) + STDOUT.print "Status: #{status}\r\n" + headers.each { |k, vs| + vs.split("\n").each { |v| + STDOUT.print "#{k}: #{v}\r\n" + } + } + STDOUT.print "\r\n" + STDOUT.flush + end + + def self.send_body(body) + body.each { |part| + STDOUT.print part + STDOUT.flush + } + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/evented_mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/evented_mongrel.rb new file mode 100644 index 0000000..0f5cbf7 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/evented_mongrel.rb @@ -0,0 +1,8 @@ +require 'swiftcore/evented_mongrel' + +module Rack + module Handler + class EventedMongrel < Handler::Mongrel + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/fastcgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/fastcgi.rb new file mode 100644 index 0000000..11e1fca --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/fastcgi.rb @@ -0,0 +1,88 @@ +require 'fcgi' +require 'socket' +require 'rack/content_length' +require 'rack/rewindable_input' + +class FCGI::Stream + alias _rack_read_without_buffer read + + def read(n, buffer=nil) + buf = _rack_read_without_buffer n + buffer.replace(buf.to_s) if buffer + buf + end +end + +module Rack + module Handler + class FastCGI + def self.run(app, options={}) + file = options[:File] and STDIN.reopen(UNIXServer.new(file)) + port = options[:Port] and STDIN.reopen(TCPServer.new(port)) + FCGI.each { |request| + serve request, app + } + end + + def self.serve(request, app) + app = Rack::ContentLength.new(app) + + env = request.env + env.delete "HTTP_CONTENT_LENGTH" + + env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" + + rack_input = RewindableInput.new(request.in) + + env.update({"rack.version" => [1,0], + "rack.input" => rack_input, + "rack.errors" => request.err, + + "rack.multithread" => false, + "rack.multiprocess" => true, + "rack.run_once" => false, + + "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" + }) + + env["QUERY_STRING"] ||= "" + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["REQUEST_PATH"] ||= "/" + env.delete "PATH_INFO" if env["PATH_INFO"] == "" + env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == "" + env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == "" + + begin + status, headers, body = app.call(env) + begin + send_headers request.out, status, headers + send_body request.out, body + ensure + body.close if body.respond_to? :close + end + ensure + rack_input.close + request.finish + end + end + + def self.send_headers(out, status, headers) + out.print "Status: #{status}\r\n" + headers.each { |k, vs| + vs.split("\n").each { |v| + out.print "#{k}: #{v}\r\n" + } + } + out.print "\r\n" + out.flush + end + + def self.send_body(out, body) + body.each { |part| + out.print part + out.flush + } + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/lsws.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/lsws.rb new file mode 100644 index 0000000..7231336 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/lsws.rb @@ -0,0 +1,55 @@ +require 'lsapi' +require 'rack/content_length' + +module Rack + module Handler + class LSWS + def self.run(app, options=nil) + while LSAPI.accept != nil + serve app + end + end + def self.serve(app) + app = Rack::ContentLength.new(app) + + env = ENV.to_hash + env.delete "HTTP_CONTENT_LENGTH" + env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" + env.update({"rack.version" => [1,0], + "rack.input" => StringIO.new($stdin.read.to_s), + "rack.errors" => $stderr, + "rack.multithread" => false, + "rack.multiprocess" => true, + "rack.run_once" => false, + "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" + }) + env["QUERY_STRING"] ||= "" + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["REQUEST_PATH"] ||= "/" + status, headers, body = app.call(env) + begin + send_headers status, headers + send_body body + ensure + body.close if body.respond_to? :close + end + end + def self.send_headers(status, headers) + print "Status: #{status}\r\n" + headers.each { |k, vs| + vs.split("\n").each { |v| + print "#{k}: #{v}\r\n" + } + } + print "\r\n" + STDOUT.flush + end + def self.send_body(body) + body.each { |part| + print part + STDOUT.flush + } + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/mongrel.rb new file mode 100644 index 0000000..3a5ef32 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/mongrel.rb @@ -0,0 +1,84 @@ +require 'mongrel' +require 'stringio' +require 'rack/content_length' +require 'rack/chunked' + +module Rack + module Handler + class Mongrel < ::Mongrel::HttpHandler + def self.run(app, options={}) + server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0', + options[:Port] || 8080) + # Acts like Rack::URLMap, utilizing Mongrel's own path finding methods. + # Use is similar to #run, replacing the app argument with a hash of + # { path=>app, ... } or an instance of Rack::URLMap. + if options[:map] + if app.is_a? Hash + app.each do |path, appl| + path = '/'+path unless path[0] == ?/ + server.register(path, Rack::Handler::Mongrel.new(appl)) + end + elsif app.is_a? URLMap + app.instance_variable_get(:@mapping).each do |(host, path, appl)| + next if !host.nil? && !options[:Host].nil? && options[:Host] != host + path = '/'+path unless path[0] == ?/ + server.register(path, Rack::Handler::Mongrel.new(appl)) + end + else + raise ArgumentError, "first argument should be a Hash or URLMap" + end + else + server.register('/', Rack::Handler::Mongrel.new(app)) + end + yield server if block_given? + server.run.join + end + + def initialize(app) + @app = Rack::Chunked.new(Rack::ContentLength.new(app)) + end + + def process(request, response) + env = {}.replace(request.params) + env.delete "HTTP_CONTENT_TYPE" + env.delete "HTTP_CONTENT_LENGTH" + + env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" + + env.update({"rack.version" => [1,0], + "rack.input" => request.body || StringIO.new(""), + "rack.errors" => $stderr, + + "rack.multithread" => true, + "rack.multiprocess" => false, # ??? + "rack.run_once" => false, + + "rack.url_scheme" => "http", + }) + env["QUERY_STRING"] ||= "" + env.delete "PATH_INFO" if env["PATH_INFO"] == "" + + status, headers, body = @app.call(env) + + begin + response.status = status.to_i + response.send_status(nil) + + headers.each { |k, vs| + vs.split("\n").each { |v| + response.header[k] = v + } + } + response.send_header + + body.each { |part| + response.write part + response.socket.flush + } + ensure + body.close if body.respond_to? :close + end + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/scgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/scgi.rb new file mode 100644 index 0000000..6c4932d --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/scgi.rb @@ -0,0 +1,59 @@ +require 'scgi' +require 'stringio' +require 'rack/content_length' +require 'rack/chunked' + +module Rack + module Handler + class SCGI < ::SCGI::Processor + attr_accessor :app + + def self.run(app, options=nil) + new(options.merge(:app=>app, + :host=>options[:Host], + :port=>options[:Port], + :socket=>options[:Socket])).listen + end + + def initialize(settings = {}) + @app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app])) + @log = Object.new + def @log.info(*args); end + def @log.error(*args); end + super(settings) + end + + def process_request(request, input_body, socket) + env = {}.replace(request) + env.delete "HTTP_CONTENT_TYPE" + env.delete "HTTP_CONTENT_LENGTH" + env["REQUEST_PATH"], env["QUERY_STRING"] = env["REQUEST_URI"].split('?', 2) + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["PATH_INFO"] = env["REQUEST_PATH"] + env["QUERY_STRING"] ||= "" + env["SCRIPT_NAME"] = "" + env.update({"rack.version" => [1,0], + "rack.input" => StringIO.new(input_body), + "rack.errors" => $stderr, + + "rack.multithread" => true, + "rack.multiprocess" => true, + "rack.run_once" => false, + + "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" + }) + status, headers, body = app.call(env) + begin + socket.write("Status: #{status}\r\n") + headers.each do |k, vs| + vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")} + end + socket.write("\r\n") + body.each {|s| socket.write(s)} + ensure + body.close if body.respond_to? :close + end + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/swiftiplied_mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/swiftiplied_mongrel.rb new file mode 100644 index 0000000..4bafd0b --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/swiftiplied_mongrel.rb @@ -0,0 +1,8 @@ +require 'swiftcore/swiftiplied_mongrel' + +module Rack + module Handler + class SwiftipliedMongrel < Handler::Mongrel + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/thin.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/thin.rb new file mode 100644 index 0000000..3d4fedf --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/thin.rb @@ -0,0 +1,18 @@ +require "thin" +require "rack/content_length" +require "rack/chunked" + +module Rack + module Handler + class Thin + def self.run(app, options={}) + app = Rack::Chunked.new(Rack::ContentLength.new(app)) + server = ::Thin::Server.new(options[:Host] || '0.0.0.0', + options[:Port] || 8080, + app) + yield server if block_given? + server.start + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/webrick.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/webrick.rb new file mode 100644 index 0000000..a8b6ff5 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/webrick.rb @@ -0,0 +1,68 @@ +require 'webrick' +require 'stringio' +require 'rack/content_length' + +module Rack + module Handler + class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet + def self.run(app, options={}) + options[:BindAddress] = options.delete(:Host) if options[:Host] + server = ::WEBrick::HTTPServer.new(options) + server.mount "/", Rack::Handler::WEBrick, app + trap(:INT) { server.shutdown } + yield server if block_given? + server.start + end + + def initialize(server, app) + super server + @app = Rack::ContentLength.new(app) + end + + def service(req, res) + env = req.meta_vars + env.delete_if { |k, v| v.nil? } + + env.update({"rack.version" => [1,0], + "rack.input" => StringIO.new(req.body.to_s), + "rack.errors" => $stderr, + + "rack.multithread" => true, + "rack.multiprocess" => false, + "rack.run_once" => false, + + "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" + }) + + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["QUERY_STRING"] ||= "" + env["REQUEST_PATH"] ||= "/" + if env["PATH_INFO"] == "" + env.delete "PATH_INFO" + else + path, n = req.request_uri.path, env["SCRIPT_NAME"].length + env["PATH_INFO"] = path[n, path.length-n] + end + + status, headers, body = @app.call(env) + begin + res.status = status.to_i + headers.each { |k, vs| + if k.downcase == "set-cookie" + res.cookies.concat vs.split("\n") + else + vs.split("\n").each { |v| + res[k] = v + } + end + } + body.each { |part| + res.body << part + } + ensure + body.close if body.respond_to? :close + end + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/head.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/head.rb new file mode 100644 index 0000000..deab822 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/head.rb @@ -0,0 +1,19 @@ +module Rack + +class Head + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + + if env["REQUEST_METHOD"] == "HEAD" + [status, headers, []] + else + [status, headers, body] + end + end +end + +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lint.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lint.rb new file mode 100644 index 0000000..bf2e978 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lint.rb @@ -0,0 +1,537 @@ +require 'rack/utils' + +module Rack + # Rack::Lint validates your application and the requests and + # responses according to the Rack spec. + + class Lint + def initialize(app) + @app = app + end + + # :stopdoc: + + class LintError < RuntimeError; end + module Assertion + def assert(message, &block) + unless block.call + raise LintError, message + end + end + end + include Assertion + + ## This specification aims to formalize the Rack protocol. You + ## can (and should) use Rack::Lint to enforce it. + ## + ## When you develop middleware, be sure to add a Lint before and + ## after to catch all mistakes. + + ## = Rack applications + + ## A Rack application is an Ruby object (not a class) that + ## responds to +call+. + def call(env=nil) + dup._call(env) + end + + def _call(env) + ## It takes exactly one argument, the *environment* + assert("No env given") { env } + check_env env + + env['rack.input'] = InputWrapper.new(env['rack.input']) + env['rack.errors'] = ErrorWrapper.new(env['rack.errors']) + + ## and returns an Array of exactly three values: + status, headers, @body = @app.call(env) + ## The *status*, + check_status status + ## the *headers*, + check_headers headers + ## and the *body*. + check_content_type status, headers + check_content_length status, headers, env + [status, headers, self] + end + + ## == The Environment + def check_env(env) + ## The environment must be an true instance of Hash (no + ## subclassing allowed) that includes CGI-like headers. + ## The application is free to modify the environment. + assert("env #{env.inspect} is not a Hash, but #{env.class}") { + env.instance_of? Hash + } + + ## + ## The environment is required to include these variables + ## (adopted from PEP333), except when they'd be empty, but see + ## below. + + ## REQUEST_METHOD:: The HTTP request method, such as + ## "GET" or "POST". This cannot ever + ## be an empty string, and so is + ## always required. + + ## SCRIPT_NAME:: The initial portion of the request + ## URL's "path" that corresponds to the + ## application object, so that the + ## application knows its virtual + ## "location". This may be an empty + ## string, if the application corresponds + ## to the "root" of the server. + + ## PATH_INFO:: The remainder of the request URL's + ## "path", designating the virtual + ## "location" of the request's target + ## within the application. This may be an + ## empty string, if the request URL targets + ## the application root and does not have a + ## trailing slash. This value may be + ## percent-encoded when I originating from + ## a URL. + + ## QUERY_STRING:: The portion of the request URL that + ## follows the ?, if any. May be + ## empty, but is always required! + + ## SERVER_NAME, SERVER_PORT:: When combined with SCRIPT_NAME and PATH_INFO, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to SERVER_NAME for reconstructing the request URL. SERVER_NAME and SERVER_PORT can never be empty strings, and so are always required. + + ## HTTP_ Variables:: Variables corresponding to the + ## client-supplied HTTP request + ## headers (i.e., variables whose + ## names begin with HTTP_). The + ## presence or absence of these + ## variables should correspond with + ## the presence or absence of the + ## appropriate HTTP header in the + ## request. + + ## In addition to this, the Rack environment must include these + ## Rack-specific variables: + + ## rack.version:: The Array [1,0], representing this version of Rack. + ## rack.url_scheme:: +http+ or +https+, depending on the request URL. + ## rack.input:: See below, the input stream. + ## rack.errors:: See below, the error stream. + ## rack.multithread:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise. + ## rack.multiprocess:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise. + ## rack.run_once:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar). + ## + + ## Additional environment specifications have approved to + ## standardized middleware APIs. None of these are required to + ## be implemented by the server. + + ## rack.session:: A hash like interface for storing request session data. + ## The store must implement: + if session = env['rack.session'] + ## store(key, value) (aliased as []=); + assert("session #{session.inspect} must respond to store and []=") { + session.respond_to?(:store) && session.respond_to?(:[]=) + } + + ## fetch(key, default = nil) (aliased as []); + assert("session #{session.inspect} must respond to fetch and []") { + session.respond_to?(:fetch) && session.respond_to?(:[]) + } + + ## delete(key); + assert("session #{session.inspect} must respond to delete") { + session.respond_to?(:delete) + } + + ## clear; + assert("session #{session.inspect} must respond to clear") { + session.respond_to?(:clear) + } + end + + ## The server or the application can store their own data in the + ## environment, too. The keys must contain at least one dot, + ## and should be prefixed uniquely. The prefix rack. + ## is reserved for use with the Rack core distribution and other + ## accepted specifications and must not be used otherwise. + ## + + %w[REQUEST_METHOD SERVER_NAME SERVER_PORT + QUERY_STRING + rack.version rack.input rack.errors + rack.multithread rack.multiprocess rack.run_once].each { |header| + assert("env missing required key #{header}") { env.include? header } + } + + ## The environment must not contain the keys + ## HTTP_CONTENT_TYPE or HTTP_CONTENT_LENGTH + ## (use the versions without HTTP_). + %w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header| + assert("env contains #{header}, must use #{header[5,-1]}") { + not env.include? header + } + } + + ## The CGI keys (named without a period) must have String values. + env.each { |key, value| + next if key.include? "." # Skip extensions + assert("env variable #{key} has non-string value #{value.inspect}") { + value.instance_of? String + } + } + + ## + ## There are the following restrictions: + + ## * rack.version must be an array of Integers. + assert("rack.version must be an Array, was #{env["rack.version"].class}") { + env["rack.version"].instance_of? Array + } + ## * rack.url_scheme must either be +http+ or +https+. + assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") { + %w[http https].include? env["rack.url_scheme"] + } + + ## * There must be a valid input stream in rack.input. + check_input env["rack.input"] + ## * There must be a valid error stream in rack.errors. + check_error env["rack.errors"] + + ## * The REQUEST_METHOD must be a valid token. + assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") { + env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/ + } + + ## * The SCRIPT_NAME, if non-empty, must start with / + assert("SCRIPT_NAME must start with /") { + !env.include?("SCRIPT_NAME") || + env["SCRIPT_NAME"] == "" || + env["SCRIPT_NAME"] =~ /\A\// + } + ## * The PATH_INFO, if non-empty, must start with / + assert("PATH_INFO must start with /") { + !env.include?("PATH_INFO") || + env["PATH_INFO"] == "" || + env["PATH_INFO"] =~ /\A\// + } + ## * The CONTENT_LENGTH, if given, must consist of digits only. + assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") { + !env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/ + } + + ## * One of SCRIPT_NAME or PATH_INFO must be + ## set. PATH_INFO should be / if + ## SCRIPT_NAME is empty. + assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") { + env["SCRIPT_NAME"] || env["PATH_INFO"] + } + ## SCRIPT_NAME never should be /, but instead be empty. + assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") { + env["SCRIPT_NAME"] != "/" + } + end + + ## === The Input Stream + ## + ## The input stream is an IO-like object which contains the raw HTTP + ## POST data. If it is a file then it must be opened in binary mode. + def check_input(input) + ## The input stream must respond to +gets+, +each+, +read+ and +rewind+. + [:gets, :each, :read, :rewind].each { |method| + assert("rack.input #{input} does not respond to ##{method}") { + input.respond_to? method + } + } + end + + class InputWrapper + include Assertion + + def initialize(input) + @input = input + end + + def size + @input.size + end + + ## * +gets+ must be called without arguments and return a string, + ## or +nil+ on EOF. + def gets(*args) + assert("rack.input#gets called with arguments") { args.size == 0 } + v = @input.gets + assert("rack.input#gets didn't return a String") { + v.nil? or v.instance_of? String + } + v + end + + ## * +read+ behaves like IO#read. Its signature is read([length, [buffer]]). + ## If given, +length+ must be an non-negative Integer (>= 0) or +nil+, and +buffer+ must + ## be a String and may not be nil. If +length+ is given and not nil, then this method + ## reads at most +length+ bytes from the input stream. If +length+ is not given or nil, + ## then this method reads all data until EOF. + ## When EOF is reached, this method returns nil if +length+ is given and not nil, or "" + ## if +length+ is not given or is nil. + ## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a + ## newly created String object. + def read(*args) + assert("rack.input#read called with too many arguments") { + args.size <= 2 + } + if args.size >= 1 + assert("rack.input#read called with non-integer and non-nil length") { + args.first.kind_of?(Integer) || args.first.nil? + } + assert("rack.input#read called with a negative length") { + args.first.nil? || args.first >= 0 + } + end + if args.size >= 2 + assert("rack.input#read called with non-String buffer") { + args[1].kind_of?(String) + } + end + + v = @input.read(*args) + + assert("rack.input#read didn't return nil or a String") { + v.nil? or v.instance_of? String + } + if args[0].nil? + assert("rack.input#read(nil) returned nil on EOF") { + !v.nil? + } + end + + v + end + + ## * +each+ must be called without arguments and only yield Strings. + def each(*args) + assert("rack.input#each called with arguments") { args.size == 0 } + @input.each { |line| + assert("rack.input#each didn't yield a String") { + line.instance_of? String + } + yield line + } + end + + ## * +rewind+ must be called without arguments. It rewinds the input + ## stream back to the beginning. It must not raise Errno::ESPIPE: + ## that is, it may not be a pipe or a socket. Therefore, handler + ## developers must buffer the input data into some rewindable object + ## if the underlying input stream is not rewindable. + def rewind(*args) + assert("rack.input#rewind called with arguments") { args.size == 0 } + assert("rack.input#rewind raised Errno::ESPIPE") { + begin + @input.rewind + true + rescue Errno::ESPIPE + false + end + } + end + + ## * +close+ must never be called on the input stream. + def close(*args) + assert("rack.input#close must not be called") { false } + end + end + + ## === The Error Stream + def check_error(error) + ## The error stream must respond to +puts+, +write+ and +flush+. + [:puts, :write, :flush].each { |method| + assert("rack.error #{error} does not respond to ##{method}") { + error.respond_to? method + } + } + end + + class ErrorWrapper + include Assertion + + def initialize(error) + @error = error + end + + ## * +puts+ must be called with a single argument that responds to +to_s+. + def puts(str) + @error.puts str + end + + ## * +write+ must be called with a single argument that is a String. + def write(str) + assert("rack.errors#write not called with a String") { str.instance_of? String } + @error.write str + end + + ## * +flush+ must be called without arguments and must be called + ## in order to make the error appear for sure. + def flush + @error.flush + end + + ## * +close+ must never be called on the error stream. + def close(*args) + assert("rack.errors#close must not be called") { false } + end + end + + ## == The Response + + ## === The Status + def check_status(status) + ## This is an HTTP status. When parsed as integer (+to_i+), it must be + ## greater than or equal to 100. + assert("Status must be >=100 seen as integer") { status.to_i >= 100 } + end + + ## === The Headers + def check_headers(header) + ## The header must respond to +each+, and yield values of key and value. + assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") { + header.respond_to? :each + } + header.each { |key, value| + ## The header keys must be Strings. + assert("header key must be a string, was #{key.class}") { + key.instance_of? String + } + ## The header must not contain a +Status+ key, + assert("header must not contain Status") { key.downcase != "status" } + ## contain keys with : or newlines in their name, + assert("header names must not contain : or \\n") { key !~ /[:\n]/ } + ## contain keys names that end in - or _, + assert("header names must not end in - or _") { key !~ /[-_]\z/ } + ## but only contain keys that consist of + ## letters, digits, _ or - and start with a letter. + assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ } + + ## The values of the header must be Strings, + assert("a header value must be a String, but the value of " + + "'#{key}' is a #{value.class}") { value.kind_of? String } + ## consisting of lines (for multiple header values, e.g. multiple + ## Set-Cookie values) seperated by "\n". + value.split("\n").each { |item| + ## The lines must not contain characters below 037. + assert("invalid header value #{key}: #{item.inspect}") { + item !~ /[\000-\037]/ + } + } + } + end + + ## === The Content-Type + def check_content_type(status, headers) + headers.each { |key, value| + ## There must be a Content-Type, except when the + ## +Status+ is 1xx, 204 or 304, in which case there must be none + ## given. + if key.downcase == "content-type" + assert("Content-Type header found in #{status} response, not allowed") { + not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i + } + return + end + } + assert("No Content-Type header found") { + Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i + } + end + + ## === The Content-Length + def check_content_length(status, headers, env) + headers.each { |key, value| + if key.downcase == 'content-length' + ## There must not be a Content-Length header when the + ## +Status+ is 1xx, 204 or 304. + assert("Content-Length header found in #{status} response, not allowed") { + not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i + } + + bytes = 0 + string_body = true + + if @body.respond_to?(:to_ary) + @body.each { |part| + unless part.kind_of?(String) + string_body = false + break + end + + bytes += Rack::Utils.bytesize(part) + } + + if env["REQUEST_METHOD"] == "HEAD" + assert("Response body was given for HEAD request, but should be empty") { + bytes == 0 + } + else + if string_body + assert("Content-Length header was #{value}, but should be #{bytes}") { + value == bytes.to_s + } + end + end + end + + return + end + } + end + + ## === The Body + def each + @closed = false + ## The Body must respond to +each+ + @body.each { |part| + ## and must only yield String values. + assert("Body yielded non-string value #{part.inspect}") { + part.instance_of? String + } + yield part + } + ## + ## The Body itself should not be an instance of String, as this will + ## break in Ruby 1.9. + ## + ## If the Body responds to +close+, it will be called after iteration. + # XXX howto: assert("Body has not been closed") { @closed } + + + ## + ## If the Body responds to +to_path+, it must return a String + ## identifying the location of a file whose contents are identical + ## to that produced by calling +each+; this may be used by the + ## server as an alternative, possibly more efficient way to + ## transport the response. + + if @body.respond_to?(:to_path) + assert("The file identified by body.to_path does not exist") { + ::File.exist? @body.to_path + } + end + + ## + ## The Body commonly is an Array of Strings, the application + ## instance itself, or a File-like object. + end + + def close + @closed = true + @body.close if @body.respond_to?(:close) + end + + # :startdoc: + + end +end + +## == Thanks +## Some parts of this specification are adopted from PEP333: Python +## Web Server Gateway Interface +## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank +## everyone involved in that effort. diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lobster.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lobster.rb new file mode 100644 index 0000000..f63f419 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lobster.rb @@ -0,0 +1,65 @@ +require 'zlib' + +require 'rack/request' +require 'rack/response' + +module Rack + # Paste has a Pony, Rack has a Lobster! + class Lobster + LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2 + P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0 + t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ + I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0]) + + LambdaLobster = lambda { |env| + if env["QUERY_STRING"].include?("flip") + lobster = LobsterString.split("\n"). + map { |line| line.ljust(42).reverse }. + join("\n") + href = "?" + else + lobster = LobsterString + href = "?flip" + end + + content = ["Lobstericious!", + "
", lobster, "
", + "flip!"] + length = content.inject(0) { |a,e| a+e.size }.to_s + [200, {"Content-Type" => "text/html", "Content-Length" => length}, content] + } + + def call(env) + req = Request.new(env) + if req.GET["flip"] == "left" + lobster = LobsterString.split("\n"). + map { |line| line.ljust(42).reverse }. + join("\n") + href = "?flip=right" + elsif req.GET["flip"] == "crash" + raise "Lobster crashed" + else + lobster = LobsterString + href = "?flip=left" + end + + res = Response.new + res.write "Lobstericious!" + res.write "
"
+      res.write lobster
+      res.write "
" + res.write "

flip!

" + res.write "

crash!

" + res.finish + end + + end +end + +if $0 == __FILE__ + require 'rack' + require 'rack/showexceptions' + Rack::Handler::WEBrick.run \ + Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), + :Port => 9292 +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lock.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lock.rb new file mode 100644 index 0000000..9323852 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lock.rb @@ -0,0 +1,16 @@ +module Rack + class Lock + FLAG = 'rack.multithread'.freeze + + def initialize(app, lock = Mutex.new) + @app, @lock = app, lock + end + + def call(env) + old, env[FLAG] = env[FLAG], false + @lock.synchronize { @app.call(env) } + ensure + env[FLAG] = old + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/methodoverride.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/methodoverride.rb new file mode 100644 index 0000000..0eed29f --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/methodoverride.rb @@ -0,0 +1,27 @@ +module Rack + class MethodOverride + HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS) + + METHOD_OVERRIDE_PARAM_KEY = "_method".freeze + HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze + + def initialize(app) + @app = app + end + + def call(env) + if env["REQUEST_METHOD"] == "POST" + req = Request.new(env) + method = req.POST[METHOD_OVERRIDE_PARAM_KEY] || + env[HTTP_METHOD_OVERRIDE_HEADER] + method = method.to_s.upcase + if HTTP_METHODS.include?(method) + env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"] + env["REQUEST_METHOD"] = method + end + end + + @app.call(env) + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mime.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mime.rb new file mode 100644 index 0000000..5a6a73a --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mime.rb @@ -0,0 +1,204 @@ +module Rack + module Mime + # Returns String with mime type if found, otherwise use +fallback+. + # +ext+ should be filename extension in the '.ext' format that + # File.extname(file) returns. + # +fallback+ may be any object + # + # Also see the documentation for MIME_TYPES + # + # Usage: + # Rack::Mime.mime_type('.foo') + # + # This is a shortcut for: + # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream') + + def mime_type(ext, fallback='application/octet-stream') + MIME_TYPES.fetch(ext, fallback) + end + module_function :mime_type + + # List of most common mime-types, selected various sources + # according to their usefulness in a webserving scope for Ruby + # users. + # + # To amend this list with your local mime.types list you can use: + # + # require 'webrick/httputils' + # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types') + # Rack::Mime::MIME_TYPES.merge!(list) + # + # To add the list mongrel provides, use: + # + # require 'mongrel/handlers' + # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES) + + MIME_TYPES = { + ".3gp" => "video/3gpp", + ".a" => "application/octet-stream", + ".ai" => "application/postscript", + ".aif" => "audio/x-aiff", + ".aiff" => "audio/x-aiff", + ".asc" => "application/pgp-signature", + ".asf" => "video/x-ms-asf", + ".asm" => "text/x-asm", + ".asx" => "video/x-ms-asf", + ".atom" => "application/atom+xml", + ".au" => "audio/basic", + ".avi" => "video/x-msvideo", + ".bat" => "application/x-msdownload", + ".bin" => "application/octet-stream", + ".bmp" => "image/bmp", + ".bz2" => "application/x-bzip2", + ".c" => "text/x-c", + ".cab" => "application/vnd.ms-cab-compressed", + ".cc" => "text/x-c", + ".chm" => "application/vnd.ms-htmlhelp", + ".class" => "application/octet-stream", + ".com" => "application/x-msdownload", + ".conf" => "text/plain", + ".cpp" => "text/x-c", + ".crt" => "application/x-x509-ca-cert", + ".css" => "text/css", + ".csv" => "text/csv", + ".cxx" => "text/x-c", + ".deb" => "application/x-debian-package", + ".der" => "application/x-x509-ca-cert", + ".diff" => "text/x-diff", + ".djv" => "image/vnd.djvu", + ".djvu" => "image/vnd.djvu", + ".dll" => "application/x-msdownload", + ".dmg" => "application/octet-stream", + ".doc" => "application/msword", + ".dot" => "application/msword", + ".dtd" => "application/xml-dtd", + ".dvi" => "application/x-dvi", + ".ear" => "application/java-archive", + ".eml" => "message/rfc822", + ".eps" => "application/postscript", + ".exe" => "application/x-msdownload", + ".f" => "text/x-fortran", + ".f77" => "text/x-fortran", + ".f90" => "text/x-fortran", + ".flv" => "video/x-flv", + ".for" => "text/x-fortran", + ".gem" => "application/octet-stream", + ".gemspec" => "text/x-script.ruby", + ".gif" => "image/gif", + ".gz" => "application/x-gzip", + ".h" => "text/x-c", + ".hh" => "text/x-c", + ".htm" => "text/html", + ".html" => "text/html", + ".ico" => "image/vnd.microsoft.icon", + ".ics" => "text/calendar", + ".ifb" => "text/calendar", + ".iso" => "application/octet-stream", + ".jar" => "application/java-archive", + ".java" => "text/x-java-source", + ".jnlp" => "application/x-java-jnlp-file", + ".jpeg" => "image/jpeg", + ".jpg" => "image/jpeg", + ".js" => "application/javascript", + ".json" => "application/json", + ".log" => "text/plain", + ".m3u" => "audio/x-mpegurl", + ".m4v" => "video/mp4", + ".man" => "text/troff", + ".mathml" => "application/mathml+xml", + ".mbox" => "application/mbox", + ".mdoc" => "text/troff", + ".me" => "text/troff", + ".mid" => "audio/midi", + ".midi" => "audio/midi", + ".mime" => "message/rfc822", + ".mml" => "application/mathml+xml", + ".mng" => "video/x-mng", + ".mov" => "video/quicktime", + ".mp3" => "audio/mpeg", + ".mp4" => "video/mp4", + ".mp4v" => "video/mp4", + ".mpeg" => "video/mpeg", + ".mpg" => "video/mpeg", + ".ms" => "text/troff", + ".msi" => "application/x-msdownload", + ".odp" => "application/vnd.oasis.opendocument.presentation", + ".ods" => "application/vnd.oasis.opendocument.spreadsheet", + ".odt" => "application/vnd.oasis.opendocument.text", + ".ogg" => "application/ogg", + ".p" => "text/x-pascal", + ".pas" => "text/x-pascal", + ".pbm" => "image/x-portable-bitmap", + ".pdf" => "application/pdf", + ".pem" => "application/x-x509-ca-cert", + ".pgm" => "image/x-portable-graymap", + ".pgp" => "application/pgp-encrypted", + ".pkg" => "application/octet-stream", + ".pl" => "text/x-script.perl", + ".pm" => "text/x-script.perl-module", + ".png" => "image/png", + ".pnm" => "image/x-portable-anymap", + ".ppm" => "image/x-portable-pixmap", + ".pps" => "application/vnd.ms-powerpoint", + ".ppt" => "application/vnd.ms-powerpoint", + ".ps" => "application/postscript", + ".psd" => "image/vnd.adobe.photoshop", + ".py" => "text/x-script.python", + ".qt" => "video/quicktime", + ".ra" => "audio/x-pn-realaudio", + ".rake" => "text/x-script.ruby", + ".ram" => "audio/x-pn-realaudio", + ".rar" => "application/x-rar-compressed", + ".rb" => "text/x-script.ruby", + ".rdf" => "application/rdf+xml", + ".roff" => "text/troff", + ".rpm" => "application/x-redhat-package-manager", + ".rss" => "application/rss+xml", + ".rtf" => "application/rtf", + ".ru" => "text/x-script.ruby", + ".s" => "text/x-asm", + ".sgm" => "text/sgml", + ".sgml" => "text/sgml", + ".sh" => "application/x-sh", + ".sig" => "application/pgp-signature", + ".snd" => "audio/basic", + ".so" => "application/octet-stream", + ".svg" => "image/svg+xml", + ".svgz" => "image/svg+xml", + ".swf" => "application/x-shockwave-flash", + ".t" => "text/troff", + ".tar" => "application/x-tar", + ".tbz" => "application/x-bzip-compressed-tar", + ".tcl" => "application/x-tcl", + ".tex" => "application/x-tex", + ".texi" => "application/x-texinfo", + ".texinfo" => "application/x-texinfo", + ".text" => "text/plain", + ".tif" => "image/tiff", + ".tiff" => "image/tiff", + ".torrent" => "application/x-bittorrent", + ".tr" => "text/troff", + ".txt" => "text/plain", + ".vcf" => "text/x-vcard", + ".vcs" => "text/x-vcalendar", + ".vrml" => "model/vrml", + ".war" => "application/java-archive", + ".wav" => "audio/x-wav", + ".wma" => "audio/x-ms-wma", + ".wmv" => "video/x-ms-wmv", + ".wmx" => "video/x-ms-wmx", + ".wrl" => "model/vrml", + ".wsdl" => "application/wsdl+xml", + ".xbm" => "image/x-xbitmap", + ".xhtml" => "application/xhtml+xml", + ".xls" => "application/vnd.ms-excel", + ".xml" => "application/xml", + ".xpm" => "image/x-xpixmap", + ".xsl" => "application/xml", + ".xslt" => "application/xslt+xml", + ".yaml" => "text/yaml", + ".yml" => "text/yaml", + ".zip" => "application/zip", + } + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mock.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mock.rb new file mode 100644 index 0000000..fdefb03 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mock.rb @@ -0,0 +1,184 @@ +require 'uri' +require 'stringio' +require 'rack/lint' +require 'rack/utils' +require 'rack/response' + +module Rack + # Rack::MockRequest helps testing your Rack application without + # actually using HTTP. + # + # After performing a request on a URL with get/post/put/delete, it + # returns a MockResponse with useful helper methods for effective + # testing. + # + # You can pass a hash with additional configuration to the + # get/post/put/delete. + # :input:: A String or IO-like to be used as rack.input. + # :fatal:: Raise a FatalWarning if the app writes to rack.errors. + # :lint:: If true, wrap the application in a Rack::Lint. + + class MockRequest + class FatalWarning < RuntimeError + end + + class FatalWarner + def puts(warning) + raise FatalWarning, warning + end + + def write(warning) + raise FatalWarning, warning + end + + def flush + end + + def string + "" + end + end + + DEFAULT_ENV = { + "rack.version" => [1,0], + "rack.input" => StringIO.new, + "rack.errors" => StringIO.new, + "rack.multithread" => true, + "rack.multiprocess" => true, + "rack.run_once" => false, + } + + def initialize(app) + @app = app + end + + def get(uri, opts={}) request("GET", uri, opts) end + def post(uri, opts={}) request("POST", uri, opts) end + def put(uri, opts={}) request("PUT", uri, opts) end + def delete(uri, opts={}) request("DELETE", uri, opts) end + + def request(method="GET", uri="", opts={}) + env = self.class.env_for(uri, opts.merge(:method => method)) + + if opts[:lint] + app = Rack::Lint.new(@app) + else + app = @app + end + + errors = env["rack.errors"] + MockResponse.new(*(app.call(env) + [errors])) + end + + # Return the Rack environment used for a request to +uri+. + def self.env_for(uri="", opts={}) + uri = URI(uri) + uri.path = "/#{uri.path}" unless uri.path[0] == ?/ + + env = DEFAULT_ENV.dup + + env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET" + env["SERVER_NAME"] = uri.host || "example.org" + env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80" + env["QUERY_STRING"] = uri.query.to_s + env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path + env["rack.url_scheme"] = uri.scheme || "http" + env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off" + + env["SCRIPT_NAME"] = opts[:script_name] || "" + + if opts[:fatal] + env["rack.errors"] = FatalWarner.new + else + env["rack.errors"] = StringIO.new + end + + if params = opts[:params] + if env["REQUEST_METHOD"] == "GET" + params = Utils.parse_nested_query(params) if params.is_a?(String) + params.update(Utils.parse_nested_query(env["QUERY_STRING"])) + env["QUERY_STRING"] = Utils.build_nested_query(params) + elsif !opts.has_key?(:input) + opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded" + if params.is_a?(Hash) + if data = Utils::Multipart.build_multipart(params) + opts[:input] = data + opts["CONTENT_LENGTH"] ||= data.length.to_s + opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}" + else + opts[:input] = Utils.build_nested_query(params) + end + else + opts[:input] = params + end + end + end + + opts[:input] ||= "" + if String === opts[:input] + env["rack.input"] = StringIO.new(opts[:input]) + else + env["rack.input"] = opts[:input] + end + + env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s + + opts.each { |field, value| + env[field] = value if String === field + } + + env + end + end + + # Rack::MockResponse provides useful helpers for testing your apps. + # Usually, you don't create the MockResponse on your own, but use + # MockRequest. + + class MockResponse + def initialize(status, headers, body, errors=StringIO.new("")) + @status = status.to_i + + @original_headers = headers + @headers = Rack::Utils::HeaderHash.new + headers.each { |field, values| + @headers[field] = values + @headers[field] = "" if values.empty? + } + + @body = "" + body.each { |part| @body << part } + + @errors = errors.string if errors.respond_to?(:string) + end + + # Status + attr_reader :status + + # Headers + attr_reader :headers, :original_headers + + def [](field) + headers[field] + end + + + # Body + attr_reader :body + + def =~(other) + @body =~ other + end + + def match(other) + @body.match other + end + + + # Errors + attr_accessor :errors + + + include Response::Helpers + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/recursive.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/recursive.rb new file mode 100644 index 0000000..bf8b965 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/recursive.rb @@ -0,0 +1,57 @@ +require 'uri' + +module Rack + # Rack::ForwardRequest gets caught by Rack::Recursive and redirects + # the current request to the app at +url+. + # + # raise ForwardRequest.new("/not-found") + # + + class ForwardRequest < Exception + attr_reader :url, :env + + def initialize(url, env={}) + @url = URI(url) + @env = env + + @env["PATH_INFO"] = @url.path + @env["QUERY_STRING"] = @url.query if @url.query + @env["HTTP_HOST"] = @url.host if @url.host + @env["HTTP_PORT"] = @url.port if @url.port + @env["rack.url_scheme"] = @url.scheme if @url.scheme + + super "forwarding to #{url}" + end + end + + # Rack::Recursive allows applications called down the chain to + # include data from other applications (by using + # rack['rack.recursive.include'][...] or raise a + # ForwardRequest to redirect internally. + + class Recursive + def initialize(app) + @app = app + end + + def call(env) + @script_name = env["SCRIPT_NAME"] + @app.call(env.merge('rack.recursive.include' => method(:include))) + rescue ForwardRequest => req + call(env.merge(req.env)) + end + + def include(env, path) + unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ || + path[@script_name.size].nil?) + raise ArgumentError, "can only include below #{@script_name}, not #{path}" + end + + env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name, + "REQUEST_METHOD" => "GET", + "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "", + "rack.input" => StringIO.new("")) + @app.call(env) + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/reloader.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/reloader.rb new file mode 100644 index 0000000..a9c566f --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/reloader.rb @@ -0,0 +1,106 @@ +# Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com +# All files in this distribution are subject to the terms of the Ruby license. + +require 'pathname' + +module Rack + + # High performant source reloader + # + # This class acts as Rack middleware. + # + # What makes it especially suited for use in a production environment is that + # any file will only be checked once and there will only be made one system + # call stat(2). + # + # Please note that this will not reload files in the background, it does so + # only when actively called. + # + # It is performing a check/reload cycle at the start of every request, but + # also respects a cool down time, during which nothing will be done. + class Reloader + def initialize(app, cooldown = 10, backend = Stat) + @app = app + @cooldown = cooldown + @last = (Time.now - cooldown) + @cache = {} + @mtimes = {} + + extend backend + end + + def call(env) + if @cooldown and Time.now > @last + @cooldown + if Thread.list.size > 1 + Thread.exclusive{ reload! } + else + reload! + end + + @last = Time.now + end + + @app.call(env) + end + + def reload!(stderr = $stderr) + rotation do |file, mtime| + previous_mtime = @mtimes[file] ||= mtime + safe_load(file, mtime, stderr) if mtime > previous_mtime + end + end + + # A safe Kernel::load, issuing the hooks depending on the results + def safe_load(file, mtime, stderr = $stderr) + load(file) + stderr.puts "#{self.class}: reloaded `#{file}'" + file + rescue LoadError, SyntaxError => ex + stderr.puts ex + ensure + @mtimes[file] = mtime + end + + module Stat + def rotation + files = [$0, *$LOADED_FEATURES].uniq + paths = ['./', *$LOAD_PATH].uniq + + files.map{|file| + next if file =~ /\.(so|bundle)$/ # cannot reload compiled files + + found, stat = figure_path(file, paths) + next unless found && stat && mtime = stat.mtime + + @cache[file] = found + + yield(found, mtime) + }.compact + end + + # Takes a relative or absolute +file+ name, a couple possible +paths+ that + # the +file+ might reside in. Returns the full path and File::Stat for the + # path. + def figure_path(file, paths) + found = @cache[file] + found = file if !found and Pathname.new(file).absolute? + found, stat = safe_stat(found) + return found, stat if found + + paths.find do |possible_path| + path = ::File.join(possible_path, file) + found, stat = safe_stat(path) + return ::File.expand_path(found), stat if found + end + end + + def safe_stat(file) + return unless file + stat = ::File.stat(file) + return file, stat if stat.file? + rescue Errno::ENOENT, Errno::ENOTDIR + @cache.delete(file) and false + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/request.rb new file mode 100644 index 0000000..4c4cf61 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/request.rb @@ -0,0 +1,248 @@ +require 'rack/utils' + +module Rack + # Rack::Request provides a convenient interface to a Rack + # environment. It is stateless, the environment +env+ passed to the + # constructor will be directly modified. + # + # req = Rack::Request.new(env) + # req.post? + # req.params["data"] + # + # The environment hash passed will store a reference to the Request object + # instantiated so that it will only instantiate if an instance of the Request + # object doesn't already exist. + + class Request + # The environment of the request. + attr_reader :env + + def initialize(env) + @env = env + end + + def body; @env["rack.input"] end + def scheme; @env["rack.url_scheme"] end + def script_name; @env["SCRIPT_NAME"].to_s end + def path_info; @env["PATH_INFO"].to_s end + def port; @env["SERVER_PORT"].to_i end + def request_method; @env["REQUEST_METHOD"] end + def query_string; @env["QUERY_STRING"].to_s end + def content_length; @env['CONTENT_LENGTH'] end + def content_type; @env['CONTENT_TYPE'] end + def session; @env['rack.session'] ||= {} end + def session_options; @env['rack.session.options'] ||= {} end + + # The media type (type/subtype) portion of the CONTENT_TYPE header + # without any media type parameters. e.g., when CONTENT_TYPE is + # "text/plain;charset=utf-8", the media-type is "text/plain". + # + # For more information on the use of media types in HTTP, see: + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 + def media_type + content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase + end + + # The media type parameters provided in CONTENT_TYPE as a Hash, or + # an empty Hash if no CONTENT_TYPE or media-type parameters were + # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8", + # this method responds with the following Hash: + # { 'charset' => 'utf-8' } + def media_type_params + return {} if content_type.nil? + content_type.split(/\s*[;,]\s*/)[1..-1]. + collect { |s| s.split('=', 2) }. + inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash } + end + + # The character set of the request body if a "charset" media type + # parameter was given, or nil if no "charset" was specified. Note + # that, per RFC2616, text/* media types that specify no explicit + # charset are to be considered ISO-8859-1. + def content_charset + media_type_params['charset'] + end + + def host + # Remove port number. + (@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '') + end + + def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end + def path_info=(s); @env["PATH_INFO"] = s.to_s end + + def get?; request_method == "GET" end + def post?; request_method == "POST" end + def put?; request_method == "PUT" end + def delete?; request_method == "DELETE" end + def head?; request_method == "HEAD" end + + # The set of form-data media-types. Requests that do not indicate + # one of the media types presents in this list will not be eligible + # for form-data / param parsing. + FORM_DATA_MEDIA_TYPES = [ + nil, + 'application/x-www-form-urlencoded', + 'multipart/form-data' + ] + + # The set of media-types. Requests that do not indicate + # one of the media types presents in this list will not be eligible + # for param parsing like soap attachments or generic multiparts + PARSEABLE_DATA_MEDIA_TYPES = [ + 'multipart/related', + 'multipart/mixed' + ] + + # Determine whether the request body contains form-data by checking + # the request media_type against registered form-data media-types: + # "application/x-www-form-urlencoded" and "multipart/form-data". The + # list of form-data media types can be modified through the + # +FORM_DATA_MEDIA_TYPES+ array. + def form_data? + FORM_DATA_MEDIA_TYPES.include?(media_type) + end + + # Determine whether the request body contains data by checking + # the request media_type against registered parse-data media-types + def parseable_data? + PARSEABLE_DATA_MEDIA_TYPES.include?(media_type) + end + + # Returns the data recieved in the query string. + def GET + if @env["rack.request.query_string"] == query_string + @env["rack.request.query_hash"] + else + @env["rack.request.query_string"] = query_string + @env["rack.request.query_hash"] = + Utils.parse_nested_query(query_string) + end + end + + # Returns the data recieved in the request body. + # + # This method support both application/x-www-form-urlencoded and + # multipart/form-data. + def POST + if @env["rack.input"].nil? + raise "Missing rack.input" + elsif @env["rack.request.form_input"].eql? @env["rack.input"] + @env["rack.request.form_hash"] + elsif form_data? || parseable_data? + @env["rack.request.form_input"] = @env["rack.input"] + unless @env["rack.request.form_hash"] = + Utils::Multipart.parse_multipart(env) + form_vars = @env["rack.input"].read + + # Fix for Safari Ajax postings that always append \0 + form_vars.sub!(/\0\z/, '') + + @env["rack.request.form_vars"] = form_vars + @env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars) + + @env["rack.input"].rewind + end + @env["rack.request.form_hash"] + else + {} + end + end + + # The union of GET and POST data. + def params + self.put? ? self.GET : self.GET.update(self.POST) + rescue EOFError => e + self.GET + end + + # shortcut for request.params[key] + def [](key) + params[key.to_s] + end + + # shortcut for request.params[key] = value + def []=(key, value) + params[key.to_s] = value + end + + # like Hash#values_at + def values_at(*keys) + keys.map{|key| params[key] } + end + + # the referer of the client or '/' + def referer + @env['HTTP_REFERER'] || '/' + end + alias referrer referer + + + def cookies + return {} unless @env["HTTP_COOKIE"] + + if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] + @env["rack.request.cookie_hash"] + else + @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] + # According to RFC 2109: + # If multiple cookies satisfy the criteria above, they are ordered in + # the Cookie header such that those with more specific Path attributes + # precede those with less specific. Ordering with respect to other + # attributes (e.g., Domain) is unspecified. + @env["rack.request.cookie_hash"] = + Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)| + h[k] = Array === v ? v.first : v + h + } + end + end + + def xhr? + @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" + end + + # Tries to return a remake of the original request URL as a string. + def url + url = scheme + "://" + url << host + + if scheme == "https" && port != 443 || + scheme == "http" && port != 80 + url << ":#{port}" + end + + url << fullpath + + url + end + + def path + script_name + path_info + end + + def fullpath + query_string.empty? ? path : "#{path}?#{query_string}" + end + + def accept_encoding + @env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part| + m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick + + if m + [m[1], (m[2] || 1.0).to_f] + else + raise "Invalid value for Accept-Encoding: #{part.inspect}" + end + end + end + + def ip + if addr = @env['HTTP_X_FORWARDED_FOR'] + addr.split(',').last.strip + else + @env['REMOTE_ADDR'] + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/response.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/response.rb new file mode 100644 index 0000000..28b4d83 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/response.rb @@ -0,0 +1,183 @@ +require 'rack/request' +require 'rack/utils' + +module Rack + # Rack::Response provides a convenient interface to create a Rack + # response. + # + # It allows setting of headers and cookies, and provides useful + # defaults (a OK response containing HTML). + # + # You can use Response#write to iteratively generate your response, + # but note that this is buffered by Rack::Response until you call + # +finish+. +finish+ however can take a block inside which calls to + # +write+ are syncronous with the Rack response. + # + # Your application's +call+ should end returning Response#finish. + + class Response + attr_accessor :length + + def initialize(body=[], status=200, header={}, &block) + @status = status + @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}. + merge(header)) + + @writer = lambda { |x| @body << x } + @block = nil + @length = 0 + + @body = [] + + if body.respond_to? :to_str + write body.to_str + elsif body.respond_to?(:each) + body.each { |part| + write part.to_s + } + else + raise TypeError, "stringable or iterable required" + end + + yield self if block_given? + end + + attr_reader :header + attr_accessor :status, :body + + def [](key) + header[key] + end + + def []=(key, value) + header[key] = value + end + + def set_cookie(key, value) + case value + when Hash + domain = "; domain=" + value[:domain] if value[:domain] + path = "; path=" + value[:path] if value[:path] + # According to RFC 2109, we need dashes here. + # N.B.: cgi.rb uses spaces... + expires = "; expires=" + value[:expires].clone.gmtime. + strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires] + secure = "; secure" if value[:secure] + httponly = "; HttpOnly" if value[:httponly] + value = value[:value] + end + value = [value] unless Array === value + cookie = Utils.escape(key) + "=" + + value.map { |v| Utils.escape v }.join("&") + + "#{domain}#{path}#{expires}#{secure}#{httponly}" + + case self["Set-Cookie"] + when Array + self["Set-Cookie"] << cookie + when String + self["Set-Cookie"] = [self["Set-Cookie"], cookie] + when nil + self["Set-Cookie"] = cookie + end + end + + def delete_cookie(key, value={}) + unless Array === self["Set-Cookie"] + self["Set-Cookie"] = [self["Set-Cookie"]].compact + end + + self["Set-Cookie"].reject! { |cookie| + cookie =~ /\A#{Utils.escape(key)}=/ + } + + set_cookie(key, + {:value => '', :path => nil, :domain => nil, + :expires => Time.at(0) }.merge(value)) + end + + def redirect(target, status=302) + self.status = status + self["Location"] = target + end + + def finish(&block) + @block = block + + if [204, 304].include?(status.to_i) + header.delete "Content-Type" + [status.to_i, header.to_hash, []] + else + [status.to_i, header.to_hash, self] + end + end + alias to_a finish # For *response + + def each(&callback) + @body.each(&callback) + @writer = callback + @block.call(self) if @block + end + + # Append to body and update Content-Length. + # + # NOTE: Do not mix #write and direct #body access! + # + def write(str) + s = str.to_s + @length += Rack::Utils.bytesize(s) + @writer.call s + + header["Content-Length"] = @length.to_s + str + end + + def close + body.close if body.respond_to?(:close) + end + + def empty? + @block == nil && @body.empty? + end + + alias headers header + + module Helpers + def invalid?; @status < 100 || @status >= 600; end + + def informational?; @status >= 100 && @status < 200; end + def successful?; @status >= 200 && @status < 300; end + def redirection?; @status >= 300 && @status < 400; end + def client_error?; @status >= 400 && @status < 500; end + def server_error?; @status >= 500 && @status < 600; end + + def ok?; @status == 200; end + def forbidden?; @status == 403; end + def not_found?; @status == 404; end + + def redirect?; [301, 302, 303, 307].include? @status; end + def empty?; [201, 204, 304].include? @status; end + + # Headers + attr_reader :headers, :original_headers + + def include?(header) + !!headers[header] + end + + def content_type + headers["Content-Type"] + end + + def content_length + cl = headers["Content-Length"] + cl ? cl.to_i : cl + end + + def location + headers["Location"] + end + end + + include Helpers + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/rewindable_input.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/rewindable_input.rb new file mode 100644 index 0000000..9e9b21f --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/rewindable_input.rb @@ -0,0 +1,98 @@ +require 'tempfile' + +module Rack + # Class which can make any IO object rewindable, including non-rewindable ones. It does + # this by buffering the data into a tempfile, which is rewindable. + # + # rack.input is required to be rewindable, so if your input stream IO is non-rewindable + # by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class + # to easily make it rewindable. + # + # Don't forget to call #close when you're done. This frees up temporary resources that + # RewindableInput uses, though it does *not* close the original IO object. + class RewindableInput + def initialize(io) + @io = io + @rewindable_io = nil + @unlinked = false + end + + def gets + make_rewindable unless @rewindable_io + @rewindable_io.gets + end + + def read(*args) + make_rewindable unless @rewindable_io + @rewindable_io.read(*args) + end + + def each(&block) + make_rewindable unless @rewindable_io + @rewindable_io.each(&block) + end + + def rewind + make_rewindable unless @rewindable_io + @rewindable_io.rewind + end + + # Closes this RewindableInput object without closing the originally + # wrapped IO oject. Cleans up any temporary resources that this RewindableInput + # has created. + # + # This method may be called multiple times. It does nothing on subsequent calls. + def close + if @rewindable_io + if @unlinked + @rewindable_io.close + else + @rewindable_io.close! + end + @rewindable_io = nil + end + end + + private + + # Ruby's Tempfile class has a bug. Subclass it and fix it. + class Tempfile < ::Tempfile + def _close + @tmpfile.close if @tmpfile + @data[1] = nil if @data + @tmpfile = nil + end + end + + def make_rewindable + # Buffer all data into a tempfile. Since this tempfile is private to this + # RewindableInput object, we chmod it so that nobody else can read or write + # it. On POSIX filesystems we also unlink the file so that it doesn't + # even have a file entry on the filesystem anymore, though we can still + # access it because we have the file handle open. + @rewindable_io = Tempfile.new('RackRewindableInput') + @rewindable_io.chmod(0000) + if filesystem_has_posix_semantics? + @rewindable_io.unlink + @unlinked = true + end + + buffer = "" + while @io.read(1024 * 4, buffer) + entire_buffer_written_out = false + while !entire_buffer_written_out + written = @rewindable_io.write(buffer) + entire_buffer_written_out = written == buffer.size + if !entire_buffer_written_out + buffer.slice!(0 .. written - 1) + end + end + end + @rewindable_io.rewind + end + + def filesystem_has_posix_semantics? + RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/ + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/abstract/id.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/abstract/id.rb new file mode 100644 index 0000000..218144c --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/abstract/id.rb @@ -0,0 +1,142 @@ +# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net +# bugrep: Andreas Zehnder + +require 'time' +require 'rack/request' +require 'rack/response' + +module Rack + + module Session + + module Abstract + + # ID sets up a basic framework for implementing an id based sessioning + # service. Cookies sent to the client for maintaining sessions will only + # contain an id reference. Only #get_session and #set_session are + # required to be overwritten. + # + # All parameters are optional. + # * :key determines the name of the cookie, by default it is + # 'rack.session' + # * :path, :domain, :expire_after, :secure, and :httponly set the related + # cookie options as by Rack::Response#add_cookie + # * :defer will not set a cookie in the response. + # * :renew (implementation dependent) will prompt the generation of a new + # session id, and migration of data to be referenced at the new id. If + # :defer is set, it will be overridden and the cookie will be set. + # * :sidbits sets the number of bits in length that a generated session + # id will be. + # + # These options can be set on a per request basis, at the location of + # env['rack.session.options']. Additionally the id of the session can be + # found within the options hash at the key :id. It is highly not + # recommended to change its value. + # + # Is Rack::Utils::Context compatible. + + class ID + DEFAULT_OPTIONS = { + :path => '/', + :domain => nil, + :expire_after => nil, + :secure => false, + :httponly => true, + :defer => false, + :renew => false, + :sidbits => 128 + } + + attr_reader :key, :default_options + def initialize(app, options={}) + @app = app + @key = options[:key] || "rack.session" + @default_options = self.class::DEFAULT_OPTIONS.merge(options) + end + + def call(env) + context(env) + end + + def context(env, app=@app) + load_session(env) + status, headers, body = app.call(env) + commit_session(env, status, headers, body) + end + + private + + # Generate a new session id using Ruby #rand. The size of the + # session id is controlled by the :sidbits option. + # Monkey patch this to use custom methods for session id generation. + + def generate_sid + "%0#{@default_options[:sidbits] / 4}x" % + rand(2**@default_options[:sidbits] - 1) + end + + # Extracts the session id from provided cookies and passes it and the + # environment to #get_session. It then sets the resulting session into + # 'rack.session', and places options and session metadata into + # 'rack.session.options'. + + def load_session(env) + request = Rack::Request.new(env) + session_id = request.cookies[@key] + + begin + session_id, session = get_session(env, session_id) + env['rack.session'] = session + rescue + env['rack.session'] = Hash.new + end + + env['rack.session.options'] = @default_options. + merge(:id => session_id) + end + + # Acquires the session from the environment and the session id from + # the session options and passes them to #set_session. If successful + # and the :defer option is not true, a cookie will be added to the + # response with the session's id. + + def commit_session(env, status, headers, body) + session = env['rack.session'] + options = env['rack.session.options'] + session_id = options[:id] + + if not session_id = set_session(env, session_id, session, options) + env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.") + [status, headers, body] + elsif options[:defer] and not options[:renew] + env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE + [status, headers, body] + else + cookie = Hash.new + cookie[:value] = session_id + cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? + response = Rack::Response.new(body, status, headers) + response.set_cookie(@key, cookie.merge(options)) + response.to_a + end + end + + # All thread safety and session retrival proceedures should occur here. + # Should return [session_id, session]. + # If nil is provided as the session id, generation of a new valid id + # should occur within. + + def get_session(env, sid) + raise '#get_session not implemented.' + end + + # All thread safety and session storage proceedures should occur here. + # Should return true or false dependant on whether or not the session + # was saved or not. + def set_session(env, sid, session, options) + raise '#set_session not implemented.' + end + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/cookie.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/cookie.rb new file mode 100644 index 0000000..eace9bd --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/cookie.rb @@ -0,0 +1,91 @@ +require 'openssl' +require 'rack/request' +require 'rack/response' + +module Rack + + module Session + + # Rack::Session::Cookie provides simple cookie based session management. + # The session is a Ruby Hash stored as base64 encoded marshalled data + # set to :key (default: rack.session). + # When the secret key is set, cookie data is checked for data integrity. + # + # Example: + # + # use Rack::Session::Cookie, :key => 'rack.session', + # :domain => 'foo.com', + # :path => '/', + # :expire_after => 2592000, + # :secret => 'change_me' + # + # All parameters are optional. + + class Cookie + + def initialize(app, options={}) + @app = app + @key = options[:key] || "rack.session" + @secret = options[:secret] + @default_options = {:domain => nil, + :path => "/", + :expire_after => nil}.merge(options) + end + + def call(env) + load_session(env) + status, headers, body = @app.call(env) + commit_session(env, status, headers, body) + end + + private + + def load_session(env) + request = Rack::Request.new(env) + session_data = request.cookies[@key] + + if @secret && session_data + session_data, digest = session_data.split("--") + session_data = nil unless digest == generate_hmac(session_data) + end + + begin + session_data = session_data.unpack("m*").first + session_data = Marshal.load(session_data) + env["rack.session"] = session_data + rescue + env["rack.session"] = Hash.new + end + + env["rack.session.options"] = @default_options.dup + end + + def commit_session(env, status, headers, body) + session_data = Marshal.dump(env["rack.session"]) + session_data = [session_data].pack("m*") + + if @secret + session_data = "#{session_data}--#{generate_hmac(session_data)}" + end + + if session_data.size > (4096 - @key.size) + env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.") + [status, headers, body] + else + options = env["rack.session.options"] + cookie = Hash.new + cookie[:value] = session_data + cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? + response = Rack::Response.new(body, status, headers) + response.set_cookie(@key, cookie.merge(options)) + response.to_a + end + end + + def generate_hmac(data) + OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data) + end + + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/memcache.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/memcache.rb new file mode 100644 index 0000000..4a65cbf --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/memcache.rb @@ -0,0 +1,109 @@ +# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net + +require 'rack/session/abstract/id' +require 'memcache' + +module Rack + module Session + # Rack::Session::Memcache provides simple cookie based session management. + # Session data is stored in memcached. The corresponding session key is + # maintained in the cookie. + # You may treat Session::Memcache as you would Session::Pool with the + # following caveats. + # + # * Setting :expire_after to 0 would note to the Memcache server to hang + # onto the session data until it would drop it according to it's own + # specifications. However, the cookie sent to the client would expire + # immediately. + # + # Note that memcache does drop data before it may be listed to expire. For + # a full description of behaviour, please see memcache's documentation. + + class Memcache < Abstract::ID + attr_reader :mutex, :pool + DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \ + :namespace => 'rack:session', + :memcache_server => 'localhost:11211' + + def initialize(app, options={}) + super + + @mutex = Mutex.new + @pool = MemCache. + new @default_options[:memcache_server], @default_options + raise 'No memcache servers' unless @pool.servers.any?{|s|s.alive?} + end + + def generate_sid + loop do + sid = super + break sid unless @pool.get(sid, true) + end + end + + def get_session(env, sid) + session = @pool.get(sid) if sid + @mutex.lock if env['rack.multithread'] + unless sid and session + env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? + session = {} + sid = generate_sid + ret = @pool.add sid, session + raise "Session collision on '#{sid.inspect}'" unless /^STORED/ =~ ret + end + session.instance_variable_set('@old', {}.merge(session)) + return [sid, session] + rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted + warn "#{self} is unable to find server." + warn $!.inspect + return [ nil, {} ] + ensure + @mutex.unlock if env['rack.multithread'] + end + + def set_session(env, session_id, new_session, options) + expiry = options[:expire_after] + expiry = expiry.nil? ? 0 : expiry + 1 + + @mutex.lock if env['rack.multithread'] + session = @pool.get(session_id) || {} + if options[:renew] or options[:drop] + @pool.delete session_id + return false if options[:drop] + session_id = generate_sid + @pool.add session_id, 0 # so we don't worry about cache miss on #set + end + old_session = new_session.instance_variable_get('@old') || {} + session = merge_sessions session_id, old_session, new_session, session + @pool.set session_id, session, expiry + return session_id + rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted + warn "#{self} is unable to find server." + warn $!.inspect + return false + ensure + @mutex.unlock if env['rack.multithread'] + end + + private + + def merge_sessions sid, old, new, cur=nil + cur ||= {} + unless Hash === old and Hash === new + warn 'Bad old or new sessions provided.' + return cur + end + + delete = old.keys - new.keys + warn "//@#{sid}: delete #{delete*','}" if $VERBOSE and not delete.empty? + delete.each{|k| cur.delete k } + + update = new.keys.select{|k| new[k] != old[k] } + warn "//@#{sid}: update #{update*','}" if $VERBOSE and not update.empty? + update.each{|k| cur[k] = new[k] } + + cur + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/pool.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/pool.rb new file mode 100644 index 0000000..f6f8740 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/pool.rb @@ -0,0 +1,100 @@ +# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net +# THANKS: +# apeiros, for session id generation, expiry setup, and threadiness +# sergio, threadiness and bugreps + +require 'rack/session/abstract/id' +require 'thread' + +module Rack + module Session + # Rack::Session::Pool provides simple cookie based session management. + # Session data is stored in a hash held by @pool. + # In the context of a multithreaded environment, sessions being + # committed to the pool is done in a merging manner. + # + # The :drop option is available in rack.session.options if you with to + # explicitly remove the session from the session cache. + # + # Example: + # myapp = MyRackApp.new + # sessioned = Rack::Session::Pool.new(myapp, + # :domain => 'foo.com', + # :expire_after => 2592000 + # ) + # Rack::Handler::WEBrick.run sessioned + + class Pool < Abstract::ID + attr_reader :mutex, :pool + DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false + + def initialize(app, options={}) + super + @pool = Hash.new + @mutex = Mutex.new + end + + def generate_sid + loop do + sid = super + break sid unless @pool.key? sid + end + end + + def get_session(env, sid) + session = @pool[sid] if sid + @mutex.lock if env['rack.multithread'] + unless sid and session + env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? + session = {} + sid = generate_sid + @pool.store sid, session + end + session.instance_variable_set('@old', {}.merge(session)) + return [sid, session] + ensure + @mutex.unlock if env['rack.multithread'] + end + + def set_session(env, session_id, new_session, options) + @mutex.lock if env['rack.multithread'] + session = @pool[session_id] + if options[:renew] or options[:drop] + @pool.delete session_id + return false if options[:drop] + session_id = generate_sid + @pool.store session_id, 0 + end + old_session = new_session.instance_variable_get('@old') || {} + session = merge_sessions session_id, old_session, new_session, session + @pool.store session_id, session + return session_id + rescue + warn "#{new_session.inspect} has been lost." + warn $!.inspect + ensure + @mutex.unlock if env['rack.multithread'] + end + + private + + def merge_sessions sid, old, new, cur=nil + cur ||= {} + unless Hash === old and Hash === new + warn 'Bad old or new sessions provided.' + return cur + end + + delete = old.keys - new.keys + warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty? + delete.each{|k| cur.delete k } + + update = new.keys.select{|k| new[k] != old[k] } + warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty? + update.each{|k| cur[k] = new[k] } + + cur + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showexceptions.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showexceptions.rb new file mode 100644 index 0000000..697bc41 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showexceptions.rb @@ -0,0 +1,349 @@ +require 'ostruct' +require 'erb' +require 'rack/request' +require 'rack/utils' + +module Rack + # Rack::ShowExceptions catches all exceptions raised from the app it + # wraps. It shows a useful backtrace with the sourcefile and + # clickable context, the whole Rack environment and the request + # data. + # + # Be careful when you use this on public-facing sites as it could + # reveal information helpful to attackers. + + class ShowExceptions + CONTEXT = 7 + + def initialize(app) + @app = app + @template = ERB.new(TEMPLATE) + end + + def call(env) + @app.call(env) + rescue StandardError, LoadError, SyntaxError => e + backtrace = pretty(env, e) + [500, + {"Content-Type" => "text/html", + "Content-Length" => backtrace.join.size.to_s}, + backtrace] + end + + def pretty(env, exception) + req = Rack::Request.new(env) + path = (req.script_name + req.path_info).squeeze("/") + + frames = exception.backtrace.map { |line| + frame = OpenStruct.new + if line =~ /(.*?):(\d+)(:in `(.*)')?/ + frame.filename = $1 + frame.lineno = $2.to_i + frame.function = $4 + + begin + lineno = frame.lineno-1 + lines = ::File.readlines(frame.filename) + frame.pre_context_lineno = [lineno-CONTEXT, 0].max + frame.pre_context = lines[frame.pre_context_lineno...lineno] + frame.context_line = lines[lineno].chomp + frame.post_context_lineno = [lineno+CONTEXT, lines.size].min + frame.post_context = lines[lineno+1..frame.post_context_lineno] + rescue + end + + frame + else + nil + end + }.compact + + env["rack.errors"].puts "#{exception.class}: #{exception.message}" + env["rack.errors"].puts exception.backtrace.map { |l| "\t" + l } + env["rack.errors"].flush + + [@template.result(binding)] + end + + def h(obj) # :nodoc: + case obj + when String + Utils.escape_html(obj) + else + Utils.escape_html(obj.inspect) + end + end + + # :stopdoc: + +# adapted from Django +# Copyright (c) 2005, the Lawrence Journal-World +# Used under the modified BSD license: +# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 +TEMPLATE = <<'HTML' + + + + + + <%=h exception.class %> at <%=h path %> + + + + + +
+

<%=h exception.class %> at <%=h path %>

+

<%=h exception.message %>

+ + + + + + +
Ruby<%=h frames.first.filename %>: in <%=h frames.first.function %>, line <%=h frames.first.lineno %>
Web<%=h req.request_method %> <%=h(req.host + path)%>
+ +

Jump to:

+ +
+ +
+

Traceback (innermost first)

+
    +<% frames.each { |frame| %> +
  • + <%=h frame.filename %>: in <%=h frame.function %> + + <% if frame.context_line %> +
    + <% if frame.pre_context %> +
      + <% frame.pre_context.each { |line| %> +
    1. <%=h line %>
    2. + <% } %> +
    + <% end %> + +
      +
    1. <%=h frame.context_line %>...
    + + <% if frame.post_context %> +
      + <% frame.post_context.each { |line| %> +
    1. <%=h line %>
    2. + <% } %> +
    + <% end %> +
    + <% end %> +
  • +<% } %> +
+
+ +
+

Request information

+ +

GET

+ <% unless req.GET.empty? %> + + + + + + + + + <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %> + + + + + <% } %> + +
VariableValue
<%=h key %>
<%=h val.inspect %>
+ <% else %> +

No GET data.

+ <% end %> + +

POST

+ <% unless req.POST.empty? %> + + + + + + + + + <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %> + + + + + <% } %> + +
VariableValue
<%=h key %>
<%=h val.inspect %>
+ <% else %> +

No POST data.

+ <% end %> + + + + <% unless req.cookies.empty? %> + + + + + + + + + <% req.cookies.each { |key, val| %> + + + + + <% } %> + +
VariableValue
<%=h key %>
<%=h val.inspect %>
+ <% else %> +

No cookie data.

+ <% end %> + +

Rack ENV

+ + + + + + + + + <% env.sort_by { |k, v| k.to_s }.each { |key, val| %> + + + + + <% } %> + +
VariableValue
<%=h key %>
<%=h val %>
+ +
+ +
+

+ You're seeing this error because you use Rack::ShowExceptions. +

+
+ + + +HTML + + # :startdoc: + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showstatus.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showstatus.rb new file mode 100644 index 0000000..28258c7 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showstatus.rb @@ -0,0 +1,106 @@ +require 'erb' +require 'rack/request' +require 'rack/utils' + +module Rack + # Rack::ShowStatus catches all empty responses the app it wraps and + # replaces them with a site explaining the error. + # + # Additional details can be put into rack.showstatus.detail + # and will be shown as HTML. If such details exist, the error page + # is always rendered, even if the reply was not empty. + + class ShowStatus + def initialize(app) + @app = app + @template = ERB.new(TEMPLATE) + end + + def call(env) + status, headers, body = @app.call(env) + headers = Utils::HeaderHash.new(headers) + empty = headers['Content-Length'].to_i <= 0 + + # client or server error, or explicit message + if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"] + req = Rack::Request.new(env) + message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s + detail = env["rack.showstatus.detail"] || message + body = @template.result(binding) + size = Rack::Utils.bytesize(body) + [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]] + else + [status, headers, body] + end + end + + def h(obj) # :nodoc: + case obj + when String + Utils.escape_html(obj) + else + Utils.escape_html(obj.inspect) + end + end + + # :stopdoc: + +# adapted from Django +# Copyright (c) 2005, the Lawrence Journal-World +# Used under the modified BSD license: +# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 +TEMPLATE = <<'HTML' + + + + + <%=h message %> at <%=h req.script_name + req.path_info %> + + + + +
+

<%=h message %> (<%= status.to_i %>)

+ + + + + + + + + +
Request Method:<%=h req.request_method %>
Request URL:<%=h req.url %>
+
+
+

<%= detail %>

+
+ +
+

+ You're seeing this error because you use Rack::ShowStatus. +

+
+ + +HTML + + # :startdoc: + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/static.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/static.rb new file mode 100644 index 0000000..168e8f8 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/static.rb @@ -0,0 +1,38 @@ +module Rack + + # The Rack::Static middleware intercepts requests for static files + # (javascript files, images, stylesheets, etc) based on the url prefixes + # passed in the options, and serves them using a Rack::File object. This + # allows a Rack stack to serve both static and dynamic content. + # + # Examples: + # use Rack::Static, :urls => ["/media"] + # will serve all requests beginning with /media from the "media" folder + # located in the current directory (ie media/*). + # + # use Rack::Static, :urls => ["/css", "/images"], :root => "public" + # will serve all requests beginning with /css or /images from the folder + # "public" in the current directory (ie public/css/* and public/images/*) + + class Static + + def initialize(app, options={}) + @app = app + @urls = options[:urls] || ["/favicon.ico"] + root = options[:root] || Dir.pwd + @file_server = Rack::File.new(root) + end + + def call(env) + path = env["PATH_INFO"] + can_serve = @urls.any? { |url| path.index(url) == 0 } + + if can_serve + @file_server.call(env) + else + @app.call(env) + end + end + + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/urlmap.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/urlmap.rb new file mode 100644 index 0000000..fcf6616 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/urlmap.rb @@ -0,0 +1,55 @@ +module Rack + # Rack::URLMap takes a hash mapping urls or paths to apps, and + # dispatches accordingly. Support for HTTP/1.1 host names exists if + # the URLs start with http:// or https://. + # + # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part + # relevant for dispatch is in the SCRIPT_NAME, and the rest in the + # PATH_INFO. This should be taken care of when you need to + # reconstruct the URL in order to create links. + # + # URLMap dispatches in such a way that the longest paths are tried + # first, since they are most specific. + + class URLMap + def initialize(map = {}) + remap(map) + end + + def remap(map) + @mapping = map.map { |location, app| + if location =~ %r{\Ahttps?://(.*?)(/.*)} + host, location = $1, $2 + else + host = nil + end + + unless location[0] == ?/ + raise ArgumentError, "paths need to start with /" + end + location = location.chomp('/') + + [host, location, app] + }.sort_by { |(h, l, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first + end + + def call(env) + path = env["PATH_INFO"].to_s.squeeze("/") + script_name = env['SCRIPT_NAME'] + hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT') + @mapping.each { |host, location, app| + next unless (hHost == host || sName == host \ + || (host.nil? && (hHost == sName || hHost == sName+':'+sPort))) + next unless location == path[0, location.size] + next unless path[location.size] == nil || path[location.size] == ?/ + + return app.call( + env.merge( + 'SCRIPT_NAME' => (script_name + location), + 'PATH_INFO' => path[location.size..-1])) + } + [404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]] + end + end +end + diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/utils.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/utils.rb new file mode 100644 index 0000000..228488c --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/utils.rb @@ -0,0 +1,520 @@ +# -*- encoding: binary -*- + +require 'set' +require 'tempfile' + +module Rack + # Rack::Utils contains a grab-bag of useful methods for writing web + # applications adopted from all kinds of Ruby libraries. + + module Utils + # Performs URI escaping so that you can construct proper + # query strings faster. Use this rather than the cgi.rb + # version since it's faster. (Stolen from Camping). + def escape(s) + s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) { + '%'+$1.unpack('H2'*$1.size).join('%').upcase + }.tr(' ', '+') + end + module_function :escape + + # Unescapes a URI escaped string. (Stolen from Camping). + def unescape(s) + s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ + [$1.delete('%')].pack('H*') + } + end + module_function :unescape + + DEFAULT_SEP = /[&;] */n + + # Stolen from Mongrel, with some small modifications: + # Parses a query string by breaking it up at the '&' + # and ';' characters. You can also use this to parse + # cookies by changing the characters used in the second + # parameter (which defaults to '&;'). + def parse_query(qs, d = nil) + params = {} + + (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| + k, v = p.split('=', 2).map { |x| unescape(x) } + + if cur = params[k] + if cur.class == Array + params[k] << v + else + params[k] = [cur, v] + end + else + params[k] = v + end + end + + return params + end + module_function :parse_query + + def parse_nested_query(qs, d = nil) + params = {} + + (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| + k, v = unescape(p).split('=', 2) + normalize_params(params, k, v) + end + + return params + end + module_function :parse_nested_query + + def normalize_params(params, name, v = nil) + name =~ %r(\A[\[\]]*([^\[\]]+)\]*) + k = $1 || '' + after = $' || '' + + return if k.empty? + + if after == "" + params[k] = v + elsif after == "[]" + params[k] ||= [] + raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) + params[k] << v + elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) + child_key = $1 + params[k] ||= [] + raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) + if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) + normalize_params(params[k].last, child_key, v) + else + params[k] << normalize_params({}, child_key, v) + end + else + params[k] ||= {} + raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) + params[k] = normalize_params(params[k], after, v) + end + + return params + end + module_function :normalize_params + + def build_query(params) + params.map { |k, v| + if v.class == Array + build_query(v.map { |x| [k, x] }) + else + "#{escape(k)}=#{escape(v)}" + end + }.join("&") + end + module_function :build_query + + def build_nested_query(value, prefix = nil) + case value + when Array + value.map { |v| + build_nested_query(v, "#{prefix}[]") + }.join("&") + when Hash + value.map { |k, v| + build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) + }.join("&") + when String + raise ArgumentError, "value must be a Hash" if prefix.nil? + "#{prefix}=#{escape(value)}" + else + prefix + end + end + module_function :build_nested_query + + # Escape ampersands, brackets and quotes to their HTML/XML entities. + def escape_html(string) + string.to_s.gsub("&", "&"). + gsub("<", "<"). + gsub(">", ">"). + gsub("'", "'"). + gsub('"', """) + end + module_function :escape_html + + def select_best_encoding(available_encodings, accept_encoding) + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + + expanded_accept_encoding = + accept_encoding.map { |m, q| + if m == "*" + (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] } + else + [[m, q]] + end + }.inject([]) { |mem, list| + mem + list + } + + encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m } + + unless encoding_candidates.include?("identity") + encoding_candidates.push("identity") + end + + expanded_accept_encoding.find_all { |m, q| + q == 0.0 + }.each { |m, _| + encoding_candidates.delete(m) + } + + return (encoding_candidates & available_encodings)[0] + end + module_function :select_best_encoding + + # Return the bytesize of String; uses String#length under Ruby 1.8 and + # String#bytesize under 1.9. + if ''.respond_to?(:bytesize) + def bytesize(string) + string.bytesize + end + else + def bytesize(string) + string.size + end + end + module_function :bytesize + + # Context allows the use of a compatible middleware at different points + # in a request handling stack. A compatible middleware must define + # #context which should take the arguments env and app. The first of which + # would be the request environment. The second of which would be the rack + # application that the request would be forwarded to. + class Context + attr_reader :for, :app + + def initialize(app_f, app_r) + raise 'running context does not respond to #context' unless app_f.respond_to? :context + @for, @app = app_f, app_r + end + + def call(env) + @for.context(env, @app) + end + + def recontext(app) + self.class.new(@for, app) + end + + def context(env, app=@app) + recontext(app).call(env) + end + end + + # A case-insensitive Hash that preserves the original case of a + # header when set. + class HeaderHash < Hash + def initialize(hash={}) + @names = {} + hash.each { |k, v| self[k] = v } + end + + def to_hash + inject({}) do |hash, (k,v)| + if v.respond_to? :to_ary + hash[k] = v.to_ary.join("\n") + else + hash[k] = v + end + hash + end + end + + def [](k) + super(@names[k] ||= @names[k.downcase]) + end + + def []=(k, v) + delete k + @names[k] = @names[k.downcase] = k + super k, v + end + + def delete(k) + canonical = k.downcase + super @names.delete(canonical) + @names.delete_if { |name,| name.downcase == canonical } + end + + def include?(k) + @names.include?(k) || @names.include?(k.downcase) + end + + alias_method :has_key?, :include? + alias_method :member?, :include? + alias_method :key?, :include? + + def merge!(other) + other.each { |k, v| self[k] = v } + self + end + + def merge(other) + hash = dup + hash.merge! other + end + end + + # Every standard HTTP code mapped to the appropriate message. + # Stolen from Mongrel. + HTTP_STATUS_CODES = { + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported' + } + + # Responses with HTTP status codes that should not have an entity body + STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304) + + # A multipart form data parser, adapted from IOWA. + # + # Usually, Rack::Request#POST takes care of calling this. + + module Multipart + class UploadedFile + # The filename, *not* including the path, of the "uploaded" file + attr_reader :original_filename + + # The content type of the "uploaded" file + attr_accessor :content_type + + def initialize(path, content_type = "text/plain", binary = false) + raise "#{path} file does not exist" unless ::File.exist?(path) + @content_type = content_type + @original_filename = ::File.basename(path) + @tempfile = Tempfile.new(@original_filename) + @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding) + @tempfile.binmode if binary + FileUtils.copy_file(path, @tempfile.path) + end + + def path + @tempfile.path + end + alias_method :local_path, :path + + def method_missing(method_name, *args, &block) #:nodoc: + @tempfile.__send__(method_name, *args, &block) + end + end + + EOL = "\r\n" + MULTIPART_BOUNDARY = "AaB03x" + + def self.parse_multipart(env) + unless env['CONTENT_TYPE'] =~ + %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n + nil + else + boundary = "--#{$1}" + + params = {} + buf = "" + content_length = env['CONTENT_LENGTH'].to_i + input = env['rack.input'] + input.rewind + + boundary_size = Utils.bytesize(boundary) + EOL.size + bufsize = 16384 + + content_length -= boundary_size + + read_buffer = '' + + status = input.read(boundary_size, read_buffer) + raise EOFError, "bad content body" unless status == boundary + EOL + + rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n + + loop { + head = nil + body = '' + filename = content_type = name = nil + + until head && buf =~ rx + if !head && i = buf.index(EOL+EOL) + head = buf.slice!(0, i+2) # First \r\n + buf.slice!(0, 2) # Second \r\n + + filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1] + content_type = head[/Content-Type: (.*)#{EOL}/ni, 1] + name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1] + + if content_type || filename + body = Tempfile.new("RackMultipart") + body.binmode if body.respond_to?(:binmode) + end + + next + end + + # Save the read body part. + if head && (boundary_size+4 < buf.size) + body << buf.slice!(0, buf.size - (boundary_size+4)) + end + + c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer) + raise EOFError, "bad content body" if c.nil? || c.empty? + buf << c + content_length -= c.size + end + + # Save the rest. + if i = buf.index(rx) + body << buf.slice!(0, i) + buf.slice!(0, boundary_size+2) + + content_length = -1 if $1 == "--" + end + + if filename == "" + # filename is blank which means no file has been selected + data = nil + elsif filename + body.rewind + + # Take the basename of the upload's original filename. + # This handles the full Windows paths given by Internet Explorer + # (and perhaps other broken user agents) without affecting + # those which give the lone filename. + filename =~ /^(?:.*[:\\\/])?(.*)/m + filename = $1 + + data = {:filename => filename, :type => content_type, + :name => name, :tempfile => body, :head => head} + elsif !filename && content_type + body.rewind + + # Generic multipart cases, not coming from a form + data = {:type => content_type, + :name => name, :tempfile => body, :head => head} + else + data = body + end + + Utils.normalize_params(params, name, data) unless data.nil? + + break if buf.empty? || content_length == -1 + } + + input.rewind + + params + end + end + + def self.build_multipart(params, first = true) + if first + unless params.is_a?(Hash) + raise ArgumentError, "value must be a Hash" + end + + multipart = false + query = lambda { |value| + case value + when Array + value.each(&query) + when Hash + value.values.each(&query) + when UploadedFile + multipart = true + end + } + params.values.each(&query) + return nil unless multipart + end + + flattened_params = Hash.new + + params.each do |key, value| + k = first ? key.to_s : "[#{key}]" + + case value + when Array + value.map { |v| + build_multipart(v, false).each { |subkey, subvalue| + flattened_params["#{k}[]#{subkey}"] = subvalue + } + } + when Hash + build_multipart(value, false).each { |subkey, subvalue| + flattened_params[k + subkey] = subvalue + } + else + flattened_params[k] = value + end + end + + if first + flattened_params.map { |name, file| + if file.respond_to?(:original_filename) + ::File.open(file.path, "rb") do |f| + f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding) +<<-EOF +--#{MULTIPART_BOUNDARY}\r +Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r +Content-Type: #{file.content_type}\r +Content-Length: #{::File.stat(file.path).size}\r +\r +#{f.read}\r +EOF + end + else +<<-EOF +--#{MULTIPART_BOUNDARY}\r +Content-Disposition: form-data; name="#{name}"\r +\r +#{file}\r +EOF + end + }.join + "--#{MULTIPART_BOUNDARY}--\r" + else + flattened_params + end + end + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack.rb deleted file mode 100644 index 371d015..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack.rb +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (C) 2007, 2008, 2009 Christian Neukirchen -# -# Rack is freely distributable under the terms of an MIT-style license. -# See COPYING or http://www.opensource.org/licenses/mit-license.php. - -path = File.expand_path(File.dirname(__FILE__)) -$:.unshift(path) unless $:.include?(path) - - -# The Rack main module, serving as a namespace for all core Rack -# modules and classes. -# -# All modules meant for use in your application are autoloaded here, -# so it should be enough just to require rack.rb in your code. - -module Rack - # The Rack protocol version number implemented. - VERSION = [1,0] - - # Return the Rack protocol version as a dotted string. - def self.version - VERSION.join(".") - end - - # Return the Rack release as a dotted string. - def self.release - "1.0" - end - - autoload :Builder, "rack/builder" - autoload :Cascade, "rack/cascade" - autoload :Chunked, "rack/chunked" - autoload :CommonLogger, "rack/commonlogger" - autoload :ConditionalGet, "rack/conditionalget" - autoload :ContentLength, "rack/content_length" - autoload :ContentType, "rack/content_type" - autoload :File, "rack/file" - autoload :Deflater, "rack/deflater" - autoload :Directory, "rack/directory" - autoload :ForwardRequest, "rack/recursive" - autoload :Handler, "rack/handler" - autoload :Head, "rack/head" - autoload :Lint, "rack/lint" - autoload :Lock, "rack/lock" - autoload :MethodOverride, "rack/methodoverride" - autoload :Mime, "rack/mime" - autoload :Recursive, "rack/recursive" - autoload :Reloader, "rack/reloader" - autoload :ShowExceptions, "rack/showexceptions" - autoload :ShowStatus, "rack/showstatus" - autoload :Static, "rack/static" - autoload :URLMap, "rack/urlmap" - autoload :Utils, "rack/utils" - - autoload :MockRequest, "rack/mock" - autoload :MockResponse, "rack/mock" - - autoload :Request, "rack/request" - autoload :Response, "rack/response" - - module Auth - autoload :Basic, "rack/auth/basic" - autoload :AbstractRequest, "rack/auth/abstract/request" - autoload :AbstractHandler, "rack/auth/abstract/handler" - autoload :OpenID, "rack/auth/openid" - module Digest - autoload :MD5, "rack/auth/digest/md5" - autoload :Nonce, "rack/auth/digest/nonce" - autoload :Params, "rack/auth/digest/params" - autoload :Request, "rack/auth/digest/request" - end - end - - module Session - autoload :Cookie, "rack/session/cookie" - autoload :Pool, "rack/session/pool" - autoload :Memcache, "rack/session/memcache" - end - - # *Adapters* connect Rack with third party web frameworks. - # - # Rack includes an adapter for Camping, see README for other - # frameworks supporting Rack in their code bases. - # - # Refer to the submodules for framework-specific calling details. - - module Adapter - autoload :Camping, "rack/adapter/camping" - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/adapter/camping.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/adapter/camping.rb deleted file mode 100644 index 63bc787..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/adapter/camping.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Rack - module Adapter - class Camping - def initialize(app) - @app = app - end - - def call(env) - env["PATH_INFO"] ||= "" - env["SCRIPT_NAME"] ||= "" - controller = @app.run(env['rack.input'], env) - h = controller.headers - h.each_pair do |k,v| - if v.kind_of? URI - h[k] = v.to_s - end - end - [controller.status, controller.headers, [controller.body.to_s]] - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb deleted file mode 100644 index 214df62..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/handler.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Rack - module Auth - # Rack::Auth::AbstractHandler implements common authentication functionality. - # - # +realm+ should be set for all handlers. - - class AbstractHandler - - attr_accessor :realm - - def initialize(app, realm=nil, &authenticator) - @app, @realm, @authenticator = app, realm, authenticator - end - - - private - - def unauthorized(www_authenticate = challenge) - return [ 401, - { 'Content-Type' => 'text/plain', - 'Content-Length' => '0', - 'WWW-Authenticate' => www_authenticate.to_s }, - [] - ] - end - - def bad_request - return [ 400, - { 'Content-Type' => 'text/plain', - 'Content-Length' => '0' }, - [] - ] - end - - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/request.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/request.rb deleted file mode 100644 index 1d9ccec..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/abstract/request.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Rack - module Auth - class AbstractRequest - - def initialize(env) - @env = env - end - - def provided? - !authorization_key.nil? - end - - def parts - @parts ||= @env[authorization_key].split(' ', 2) - end - - def scheme - @scheme ||= parts.first.downcase.to_sym - end - - def params - @params ||= parts.last - end - - - private - - AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION'] - - def authorization_key - @authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) } - end - - end - - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/basic.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/basic.rb deleted file mode 100644 index 9557224..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/basic.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'rack/auth/abstract/handler' -require 'rack/auth/abstract/request' - -module Rack - module Auth - # Rack::Auth::Basic implements HTTP Basic Authentication, as per RFC 2617. - # - # Initialize with the Rack application that you want protecting, - # and a block that checks if a username and password pair are valid. - # - # See also: example/protectedlobster.rb - - class Basic < AbstractHandler - - def call(env) - auth = Basic::Request.new(env) - - return unauthorized unless auth.provided? - - return bad_request unless auth.basic? - - if valid?(auth) - env['REMOTE_USER'] = auth.username - - return @app.call(env) - end - - unauthorized - end - - - private - - def challenge - 'Basic realm="%s"' % realm - end - - def valid?(auth) - @authenticator.call(*auth.credentials) - end - - class Request < Auth::AbstractRequest - def basic? - :basic == scheme - end - - def credentials - @credentials ||= params.unpack("m*").first.split(/:/, 2) - end - - def username - credentials.first - end - end - - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/md5.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/md5.rb deleted file mode 100644 index e579dc9..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/md5.rb +++ /dev/null @@ -1,124 +0,0 @@ -require 'rack/auth/abstract/handler' -require 'rack/auth/digest/request' -require 'rack/auth/digest/params' -require 'rack/auth/digest/nonce' -require 'digest/md5' - -module Rack - module Auth - module Digest - # Rack::Auth::Digest::MD5 implements the MD5 algorithm version of - # HTTP Digest Authentication, as per RFC 2617. - # - # Initialize with the [Rack] application that you want protecting, - # and a block that looks up a plaintext password for a given username. - # - # +opaque+ needs to be set to a constant base64/hexadecimal string. - # - class MD5 < AbstractHandler - - attr_accessor :opaque - - attr_writer :passwords_hashed - - def initialize(*args) - super - @passwords_hashed = nil - end - - def passwords_hashed? - !!@passwords_hashed - end - - def call(env) - auth = Request.new(env) - - unless auth.provided? - return unauthorized - end - - if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth) - return bad_request - end - - if valid?(auth) - if auth.nonce.stale? - return unauthorized(challenge(:stale => true)) - else - env['REMOTE_USER'] = auth.username - - return @app.call(env) - end - end - - unauthorized - end - - - private - - QOP = 'auth'.freeze - - def params(hash = {}) - Params.new do |params| - params['realm'] = realm - params['nonce'] = Nonce.new.to_s - params['opaque'] = H(opaque) - params['qop'] = QOP - - hash.each { |k, v| params[k] = v } - end - end - - def challenge(hash = {}) - "Digest #{params(hash)}" - end - - def valid?(auth) - valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth) - end - - def valid_qop?(auth) - QOP == auth.qop - end - - def valid_opaque?(auth) - H(opaque) == auth.opaque - end - - def valid_nonce?(auth) - auth.nonce.valid? - end - - def valid_digest?(auth) - digest(auth, @authenticator.call(auth.username)) == auth.response - end - - def md5(data) - ::Digest::MD5.hexdigest(data) - end - - alias :H :md5 - - def KD(secret, data) - H([secret, data] * ':') - end - - def A1(auth, password) - [ auth.username, auth.realm, password ] * ':' - end - - def A2(auth) - [ auth.method, auth.uri ] * ':' - end - - def digest(auth, password) - password_hash = passwords_hashed? ? password : H(A1(auth, password)) - - KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, QOP, H(A2(auth)) ] * ':') - end - - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb deleted file mode 100644 index dbe109f..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/nonce.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'digest/md5' - -module Rack - module Auth - module Digest - # Rack::Auth::Digest::Nonce is the default nonce generator for the - # Rack::Auth::Digest::MD5 authentication handler. - # - # +private_key+ needs to set to a constant string. - # - # +time_limit+ can be optionally set to an integer (number of seconds), - # to limit the validity of the generated nonces. - - class Nonce - - class << self - attr_accessor :private_key, :time_limit - end - - def self.parse(string) - new(*string.unpack("m*").first.split(' ', 2)) - end - - def initialize(timestamp = Time.now, given_digest = nil) - @timestamp, @given_digest = timestamp.to_i, given_digest - end - - def to_s - [([ @timestamp, digest ] * ' ')].pack("m*").strip - end - - def digest - ::Digest::MD5.hexdigest([ @timestamp, self.class.private_key ] * ':') - end - - def valid? - digest == @given_digest - end - - def stale? - !self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit - end - - def fresh? - !stale? - end - - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/params.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/params.rb deleted file mode 100644 index 730e2ef..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/params.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Rack - module Auth - module Digest - class Params < Hash - - def self.parse(str) - split_header_value(str).inject(new) do |header, param| - k, v = param.split('=', 2) - header[k] = dequote(v) - header - end - end - - def self.dequote(str) # From WEBrick::HTTPUtils - ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup - ret.gsub!(/\\(.)/, "\\1") - ret - end - - def self.split_header_value(str) - str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] } - end - - def initialize - super - - yield self if block_given? - end - - def [](k) - super k.to_s - end - - def []=(k, v) - super k.to_s, v.to_s - end - - UNQUOTED = ['qop', 'nc', 'stale'] - - def to_s - inject([]) do |parts, (k, v)| - parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v)) - parts - end.join(', ') - end - - def quote(str) # From WEBrick::HTTPUtils - '"' << str.gsub(/[\\\"]/o, "\\\1") << '"' - end - - end - end - end -end - diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/request.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/request.rb deleted file mode 100644 index a8aa3bf..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/digest/request.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'rack/auth/abstract/request' -require 'rack/auth/digest/params' -require 'rack/auth/digest/nonce' - -module Rack - module Auth - module Digest - class Request < Auth::AbstractRequest - - def method - @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD'] - end - - def digest? - :digest == scheme - end - - def correct_uri? - (@env['SCRIPT_NAME'].to_s + @env['PATH_INFO'].to_s) == uri - end - - def nonce - @nonce ||= Nonce.parse(params['nonce']) - end - - def params - @params ||= Params.parse(parts.last) - end - - def method_missing(sym) - if params.has_key? key = sym.to_s - return params[key] - end - super - end - - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/openid.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/openid.rb deleted file mode 100644 index c5f6a51..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/auth/openid.rb +++ /dev/null @@ -1,480 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net - -gem 'ruby-openid', '~> 2' if defined? Gem -require 'rack/request' -require 'rack/utils' -require 'rack/auth/abstract/handler' -require 'uri' -require 'openid' #gem -require 'openid/extension' #gem -require 'openid/store/memory' #gem - -module Rack - class Request - def openid_request - @env['rack.auth.openid.request'] - end - - def openid_response - @env['rack.auth.openid.response'] - end - end - - module Auth - - # Rack::Auth::OpenID provides a simple method for setting up an OpenID - # Consumer. It requires the ruby-openid library from janrain to operate, - # as well as a rack method of session management. - # - # The ruby-openid home page is at http://openidenabled.com/ruby-openid/. - # - # The OpenID specifications can be found at - # http://openid.net/specs/openid-authentication-1_1.html - # and - # http://openid.net/specs/openid-authentication-2_0.html. Documentation - # for published OpenID extensions and related topics can be found at - # http://openid.net/developers/specs/. - # - # It is recommended to read through the OpenID spec, as well as - # ruby-openid's documentation, to understand what exactly goes on. However - # a setup as simple as the presented examples is enough to provide - # Consumer functionality. - # - # This library strongly intends to utilize the OpenID 2.0 features of the - # ruby-openid library, which provides OpenID 1.0 compatiblity. - # - # NOTE: Due to the amount of data that this library stores in the - # session, Rack::Session::Cookie may fault. - - class OpenID - - class NoSession < RuntimeError; end - class BadExtension < RuntimeError; end - # Required for ruby-openid - ValidStatus = [:success, :setup_needed, :cancel, :failure] - - # = Arguments - # - # The first argument is the realm, identifying the site they are trusting - # with their identity. This is required, also treated as the trust_root - # in OpenID 1.x exchanges. - # - # The optional second argument is a hash of options. - # - # == Options - # - # :return_to defines the url to return to after the client - # authenticates with the openid service provider. This url should point - # to where Rack::Auth::OpenID is mounted. If :return_to is not - # provided, return_to will be the current url which allows flexibility - # with caveats. - # - # :session_key defines the key to the session hash in the env. - # It defaults to 'rack.session'. - # - # :openid_param defines at what key in the request parameters to - # find the identifier to resolve. As per the 2.0 spec, the default is - # 'openid_identifier'. - # - # :store defined what OpenID Store to use for persistant - # information. By default a Store::Memory will be used. - # - # :immediate as true will make initial requests to be of an - # immediate type. This is false by default. See OpenID specification - # documentation. - # - # :extensions should be a hash of openid extension - # implementations. The key should be the extension main module, the value - # should be an array of arguments for extension::Request.new. - # The hash is iterated over and passed to #add_extension for processing. - # Please see #add_extension for further documentation. - # - # == Examples - # - # simple_oid = OpenID.new('http://mysite.com/') - # - # return_oid = OpenID.new('http://mysite.com/', { - # :return_to => 'http://mysite.com/openid' - # }) - # - # complex_oid = OpenID.new('http://mysite.com/', - # :immediate => true, - # :extensions => { - # ::OpenID::SReg => [['email'],['nickname']] - # } - # ) - # - # = Advanced - # - # Most of the functionality of this library is encapsulated such that - # expansion and overriding functions isn't difficult nor tricky. - # Alternately, to avoid opening up singleton objects or subclassing, a - # wrapper rack middleware can be composed to act upon Auth::OpenID's - # responses. See #check and #finish for locations of pertinent data. - # - # == Responses - # - # To change the responses that Auth::OpenID returns, override the methods - # #redirect, #bad_request, #unauthorized, #access_denied, and - # #foreign_server_failure. - # - # Additionally #confirm_post_params is used when the URI would exceed - # length limits on a GET request when doing the initial verification - # request. - # - # == Processing - # - # To change methods of processing completed transactions, override the - # methods #success, #setup_needed, #cancel, and #failure. Please ensure - # the returned object is a rack compatible response. - # - # The first argument is an OpenID::Response, the second is a - # Rack::Request of the current request, the last is the hash used in - # ruby-openid handling, which can be found manually at - # env['rack.session'][:openid]. - # - # This is useful if you wanted to expand the processing done, such as - # setting up user accounts. - # - # oid_app = Rack::Auth::OpenID.new realm, :return_to => return_to - # def oid_app.success oid, request, session - # user = Models::User[oid.identity_url] - # user ||= Models::User.create_from_openid oid - # request['rack.session'][:user] = user.id - # redirect MyApp.site_home - # end - # - # site_map['/openid'] = oid_app - # map = Rack::URLMap.new site_map - # ... - - def initialize(realm, options={}) - realm = URI(realm) - raise ArgumentError, "Invalid realm: #{realm}" \ - unless realm.absolute? \ - and realm.fragment.nil? \ - and realm.scheme =~ /^https?$/ \ - and realm.host =~ /^(\*\.)?#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+/ - realm.path = '/' if realm.path.empty? - @realm = realm.to_s - - if ruri = options[:return_to] - ruri = URI(ruri) - raise ArgumentError, "Invalid return_to: #{ruri}" \ - unless ruri.absolute? \ - and ruri.scheme =~ /^https?$/ \ - and ruri.fragment.nil? - raise ArgumentError, "return_to #{ruri} not within realm #{realm}" \ - unless self.within_realm?(ruri) - @return_to = ruri.to_s - end - - @session_key = options[:session_key] || 'rack.session' - @openid_param = options[:openid_param] || 'openid_identifier' - @store = options[:store] || ::OpenID::Store::Memory.new - @immediate = !!options[:immediate] - - @extensions = {} - if extensions = options.delete(:extensions) - extensions.each do |ext, args| - add_extension ext, *args - end - end - - # Undocumented, semi-experimental - @anonymous = !!options[:anonymous] - end - - attr_reader :realm, :return_to, :session_key, :openid_param, :store, - :immediate, :extensions - - # Sets up and uses session data at :openid within the session. - # Errors in this setup will raise a NoSession exception. - # - # If the parameter 'openid.mode' is set, which implies a followup from - # the openid server, processing is passed to #finish and the result is - # returned. However, if there is no appropriate openid information in the - # session, a 400 error is returned. - # - # If the parameter specified by options[:openid_param] is - # present, processing is passed to #check and the result is returned. - # - # If neither of these conditions are met, #unauthorized is called. - - def call(env) - env['rack.auth.openid'] = self - env_session = env[@session_key] - unless env_session and env_session.is_a?(Hash) - raise NoSession, 'No compatible session' - end - # let us work in our own namespace... - session = (env_session[:openid] ||= {}) - unless session and session.is_a?(Hash) - raise NoSession, 'Incompatible openid session' - end - - request = Rack::Request.new(env) - consumer = ::OpenID::Consumer.new(session, @store) - - if mode = request.GET['openid.mode'] - if session.key?(:openid_param) - finish(consumer, session, request) - else - bad_request - end - elsif request.GET[@openid_param] - check(consumer, session, request) - else - unauthorized - end - end - - # As the first part of OpenID consumer action, #check retrieves the data - # required for completion. - # - # If all parameters fit within the max length of a URI, a 303 redirect - # will be returned. Otherwise #confirm_post_params will be called. - # - # Any messages from OpenID's request are logged to env['rack.errors'] - # - # env['rack.auth.openid.request'] is the openid checkid request - # instance. - # - # session[:openid_param] is set to the openid identifier - # provided by the user. - # - # session[:return_to] is set to the return_to uri given to the - # identity provider. - - def check(consumer, session, req) - oid = consumer.begin(req.GET[@openid_param], @anonymous) - req.env['rack.auth.openid.request'] = oid - req.env['rack.errors'].puts(oid.message) - p oid if $DEBUG - - ## Extension support - extensions.each do |ext,args| - oid.add_extension(ext::Request.new(*args)) - end - - session[:openid_param] = req.GET[openid_param] - return_to_uri = return_to ? return_to : req.url - session[:return_to] = return_to_uri - immediate = session.key?(:setup_needed) ? false : immediate - - if oid.send_redirect?(realm, return_to_uri, immediate) - uri = oid.redirect_url(realm, return_to_uri, immediate) - redirect(uri) - else - confirm_post_params(oid, realm, return_to_uri, immediate) - end - rescue ::OpenID::DiscoveryFailure => e - # thrown from inside OpenID::Consumer#begin by yadis stuff - req.env['rack.errors'].puts([e.message, *e.backtrace]*"\n") - return foreign_server_failure - end - - # This is the final portion of authentication. - # If successful, a redirect to the realm is be returned. - # Data gathered from extensions are stored in session[:openid] with the - # extension's namespace uri as the key. - # - # Any messages from OpenID's response are logged to env['rack.errors'] - # - # env['rack.auth.openid.response'] will contain the openid - # response. - - def finish(consumer, session, req) - oid = consumer.complete(req.GET, req.url) - req.env['rack.auth.openid.response'] = oid - req.env['rack.errors'].puts(oid.message) - p oid if $DEBUG - - raise unless ValidStatus.include?(oid.status) - __send__(oid.status, oid, req, session) - end - - # The first argument should be the main extension module. - # The extension module should contain the constants: - # * class Request, should have OpenID::Extension as an ancestor - # * class Response, should have OpenID::Extension as an ancestor - # * string NS_URI, which defining the namespace of the extension - # - # All trailing arguments will be passed to extension::Request.new in - # #check. - # The openid response will be passed to - # extension::Response#from_success_response, #get_extension_args will be - # called on the result to attain the gathered data. - # - # This method returns the key at which the response data will be found in - # the session, which is the namespace uri by default. - - def add_extension(ext, *args) - raise BadExtension unless valid_extension?(ext) - extensions[ext] = args - return ext::NS_URI - end - - # Checks the validitity, in the context of usage, of a submitted - # extension. - - def valid_extension?(ext) - if not %w[NS_URI Request Response].all?{|c| ext.const_defined?(c) } - raise ArgumentError, 'Extension is missing constants.' - elsif not ext::Response.respond_to?(:from_success_response) - raise ArgumentError, 'Response is missing required method.' - end - return true - rescue - return false - end - - # Checks the provided uri to ensure it'd be considered within the realm. - # is currently not compatible with wildcard realms. - - def within_realm? uri - uri = URI.parse(uri.to_s) - realm = URI.parse(self.realm) - return false unless uri.absolute? - return false unless uri.path[0, realm.path.size] == realm.path - return false unless uri.host == realm.host or realm.host[/^\*\./] - # for wildcard support, is awkward with URI limitations - realm_match = Regexp.escape(realm.host). - sub(/^\*\./,"^#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+.")+'$' - return false unless uri.host.match(realm_match) - return true - end - alias_method :include?, :within_realm? - - protected - - ### These methods define some of the boilerplate responses. - - # Returns an html form page for posting to an Identity Provider if the - # GET request would exceed the upper URI length limit. - - def confirm_post_params(oid, realm, return_to, immediate) - Rack::Response.new.finish do |r| - r.write 'Confirm...' - r.write oid.form_markup(realm, return_to, immediate) - r.write '' - end - end - - # Returns a 303 redirect with the destination of that provided by the - # argument. - - def redirect(uri) - [ 303, {'Content-Length'=>'0', 'Content-Type'=>'text/plain', - 'Location' => uri}, - [] ] - end - - # Returns an empty 400 response. - - def bad_request - [ 400, {'Content-Type'=>'text/plain', 'Content-Length'=>'0'}, - [''] ] - end - - # Returns a basic unauthorized 401 response. - - def unauthorized - [ 401, {'Content-Type' => 'text/plain', 'Content-Length' => '13'}, - ['Unauthorized.'] ] - end - - # Returns a basic access denied 403 response. - - def access_denied - [ 403, {'Content-Type' => 'text/plain', 'Content-Length' => '14'}, - ['Access denied.'] ] - end - - # Returns a 503 response to be used if communication with the remote - # OpenID server fails. - - def foreign_server_failure - [ 503, {'Content-Type'=>'text/plain', 'Content-Length' => '23'}, - ['Foreign server failure.'] ] - end - - private - - ### These methods are called after a transaction is completed, depending - # on its outcome. These should all return a rack compatible response. - # You'd want to override these to provide additional functionality. - - # Called to complete processing on a successful transaction. - # Within the openid session, :openid_identity and :openid_identifier are - # set to the user friendly and the standard representation of the - # validated identity. All other data in the openid session is cleared. - - def success(oid, request, session) - session.clear - session[:openid_identity] = oid.display_identifier - session[:openid_identifier] = oid.identity_url - extensions.keys.each do |ext| - label = ext.name[/[^:]+$/].downcase - response = ext::Response.from_success_response(oid) - session[label] = response.data - end - redirect(realm) - end - - # Called if the Identity Provider indicates further setup by the user is - # required. - # The identifier is retrived from the openid session at :openid_param. - # And :setup_needed is set to true to prevent looping. - - def setup_needed(oid, request, session) - identifier = session[:openid_param] - session[:setup_needed] = true - redirect req.script_name + '?' + openid_param + '=' + identifier - end - - # Called if the user indicates they wish to cancel identification. - # Data within openid session is cleared. - - def cancel(oid, request, session) - session.clear - access_denied - end - - # Called if the Identity Provider indicates the user is unable to confirm - # their identity. Data within the openid session is left alone, in case - # of swarm auth attacks. - - def failure(oid, request, session) - unauthorized - end - end - - # A class developed out of the request to use OpenID as an authentication - # middleware. The request will be sent to the OpenID instance unless the - # block evaluates to true. For example in rackup, you can use it as such: - # - # use Rack::Session::Pool - # use Rack::Auth::OpenIDAuth, realm, openid_options do |env| - # env['rack.session'][:authkey] == a_string - # end - # run RackApp - # - # Or simply: - # - # app = Rack::Auth::OpenIDAuth.new app, realm, openid_options, &auth - - class OpenIDAuth < Rack::Auth::AbstractHandler - attr_reader :oid - def initialize(app, realm, options={}, &auth) - @oid = OpenID.new(realm, options) - super(app, &auth) - end - - def call(env) - to = auth.call(env) ? @app : @oid - to.call env - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/builder.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/builder.rb deleted file mode 100644 index 295235e..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/builder.rb +++ /dev/null @@ -1,63 +0,0 @@ -module Rack - # Rack::Builder implements a small DSL to iteratively construct Rack - # applications. - # - # Example: - # - # app = Rack::Builder.new { - # use Rack::CommonLogger - # use Rack::ShowExceptions - # map "/lobster" do - # use Rack::Lint - # run Rack::Lobster.new - # end - # } - # - # Or - # - # app = Rack::Builder.app do - # use Rack::CommonLogger - # lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] } - # end - # - # +use+ adds a middleware to the stack, +run+ dispatches to an application. - # You can use +map+ to construct a Rack::URLMap in a convenient way. - - class Builder - def initialize(&block) - @ins = [] - instance_eval(&block) if block_given? - end - - def self.app(&block) - self.new(&block).to_app - end - - def use(middleware, *args, &block) - @ins << lambda { |app| middleware.new(app, *args, &block) } - end - - def run(app) - @ins << app #lambda { |nothing| app } - end - - def map(path, &block) - if @ins.last.kind_of? Hash - @ins.last[path] = self.class.new(&block).to_app - else - @ins << {} - map(path, &block) - end - end - - def to_app - @ins[-1] = Rack::URLMap.new(@ins.last) if Hash === @ins.last - inner_app = @ins.last - @ins[0...-1].reverse.inject(inner_app) { |a, e| e.call(a) } - end - - def call(env) - to_app.call(env) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/cascade.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/cascade.rb deleted file mode 100644 index a038aa1..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/cascade.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Rack - # Rack::Cascade tries an request on several apps, and returns the - # first response that is not 404 (or in a list of configurable - # status codes). - - class Cascade - attr_reader :apps - - def initialize(apps, catch=404) - @apps = apps - @catch = [*catch] - end - - def call(env) - status = headers = body = nil - raise ArgumentError, "empty cascade" if @apps.empty? - @apps.each { |app| - begin - status, headers, body = app.call(env) - break unless @catch.include?(status.to_i) - end - } - [status, headers, body] - end - - def add app - @apps << app - end - - def include? app - @apps.include? app - end - - alias_method :<<, :add - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/chunked.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/chunked.rb deleted file mode 100644 index 280d89d..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/chunked.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'rack/utils' - -module Rack - - # Middleware that applies chunked transfer encoding to response bodies - # when the response does not include a Content-Length header. - class Chunked - include Rack::Utils - - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = HeaderHash.new(headers) - - if env['HTTP_VERSION'] == 'HTTP/1.0' || - STATUS_WITH_NO_ENTITY_BODY.include?(status) || - headers['Content-Length'] || - headers['Transfer-Encoding'] - [status, headers.to_hash, body] - else - dup.chunk(status, headers, body) - end - end - - def chunk(status, headers, body) - @body = body - headers.delete('Content-Length') - headers['Transfer-Encoding'] = 'chunked' - [status, headers.to_hash, self] - end - - def each - term = "\r\n" - @body.each do |chunk| - size = bytesize(chunk) - next if size == 0 - yield [size.to_s(16), term, chunk, term].join - end - yield ["0", term, "", term].join - end - - def close - @body.close if @body.respond_to?(:close) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/commonlogger.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/commonlogger.rb deleted file mode 100644 index 5e68ac6..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/commonlogger.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Rack - # Rack::CommonLogger forwards every request to an +app+ given, and - # logs a line in the Apache common log format to the +logger+, or - # rack.errors by default. - - class CommonLogger - def initialize(app, logger=nil) - @app = app - @logger = logger - end - - def call(env) - dup._call(env) - end - - def _call(env) - @env = env - @logger ||= self - @time = Time.now - @status, @header, @body = @app.call(env) - [@status, @header, self] - end - - def close - @body.close if @body.respond_to? :close - end - - # By default, log to rack.errors. - def <<(str) - @env["rack.errors"].write(str) - @env["rack.errors"].flush - end - - def each - length = 0 - @body.each { |part| - length += part.size - yield part - } - - @now = Time.now - - # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common - # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 - - # %{%s - %s [%s] "%s %s%s %s" %d %s\n} % - @logger << %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} % - [ - @env['HTTP_X_FORWARDED_FOR'] || @env["REMOTE_ADDR"] || "-", - @env["REMOTE_USER"] || "-", - @now.strftime("%d/%b/%Y %H:%M:%S"), - @env["REQUEST_METHOD"], - @env["PATH_INFO"], - @env["QUERY_STRING"].empty? ? "" : "?"+@env["QUERY_STRING"], - @env["HTTP_VERSION"], - @status.to_s[0..3], - (length.zero? ? "-" : length.to_s), - @now - @time - ] - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/conditionalget.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/conditionalget.rb deleted file mode 100644 index 046ebdb..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/conditionalget.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'rack/utils' - -module Rack - - # Middleware that enables conditional GET using If-None-Match and - # If-Modified-Since. The application should set either or both of the - # Last-Modified or Etag response headers according to RFC 2616. When - # either of the conditions is met, the response body is set to be zero - # length and the response status is set to 304 Not Modified. - # - # Applications that defer response body generation until the body's each - # message is received will avoid response body generation completely when - # a conditional GET matches. - # - # Adapted from Michael Klishin's Merb implementation: - # http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb - class ConditionalGet - def initialize(app) - @app = app - end - - def call(env) - return @app.call(env) unless %w[GET HEAD].include?(env['REQUEST_METHOD']) - - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - if etag_matches?(env, headers) || modified_since?(env, headers) - status = 304 - headers.delete('Content-Type') - headers.delete('Content-Length') - body = [] - end - [status, headers, body] - end - - private - def etag_matches?(env, headers) - etag = headers['Etag'] and etag == env['HTTP_IF_NONE_MATCH'] - end - - def modified_since?(env, headers) - last_modified = headers['Last-Modified'] and - last_modified == env['HTTP_IF_MODIFIED_SINCE'] - end - end - -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_length.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_length.rb deleted file mode 100644 index 1e56d43..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_length.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'rack/utils' - -module Rack - # Sets the Content-Length header on responses with fixed-length bodies. - class ContentLength - include Rack::Utils - - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = HeaderHash.new(headers) - - if !STATUS_WITH_NO_ENTITY_BODY.include?(status) && - !headers['Content-Length'] && - !headers['Transfer-Encoding'] && - (body.respond_to?(:to_ary) || body.respond_to?(:to_str)) - - body = [body] if body.respond_to?(:to_str) # rack 0.4 compat - length = body.to_ary.inject(0) { |len, part| len + bytesize(part) } - headers['Content-Length'] = length.to_s - end - - [status, headers, body] - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_type.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_type.rb deleted file mode 100644 index 0c1e1ca..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/content_type.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'rack/utils' - -module Rack - - # Sets the Content-Type header on responses which don't have one. - # - # Builder Usage: - # use Rack::ContentType, "text/plain" - # - # When no content type argument is provided, "text/html" is assumed. - class ContentType - def initialize(app, content_type = "text/html") - @app, @content_type = app, content_type - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - headers['Content-Type'] ||= @content_type - [status, headers.to_hash, body] - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/deflater.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/deflater.rb deleted file mode 100644 index 14137a9..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/deflater.rb +++ /dev/null @@ -1,96 +0,0 @@ -require "zlib" -require "stringio" -require "time" # for Time.httpdate -require 'rack/utils' - -module Rack - class Deflater - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - - # Skip compressing empty entity body responses and responses with - # no-transform set. - if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) || - headers['Cache-Control'].to_s =~ /\bno-transform\b/ - return [status, headers, body] - end - - request = Request.new(env) - - encoding = Utils.select_best_encoding(%w(gzip deflate identity), - request.accept_encoding) - - # Set the Vary HTTP header. - vary = headers["Vary"].to_s.split(",").map { |v| v.strip } - unless vary.include?("*") || vary.include?("Accept-Encoding") - headers["Vary"] = vary.push("Accept-Encoding").join(",") - end - - case encoding - when "gzip" - headers['Content-Encoding'] = "gzip" - headers.delete('Content-Length') - mtime = headers.key?("Last-Modified") ? - Time.httpdate(headers["Last-Modified"]) : Time.now - [status, headers, GzipStream.new(body, mtime)] - when "deflate" - headers['Content-Encoding'] = "deflate" - headers.delete('Content-Length') - [status, headers, DeflateStream.new(body)] - when "identity" - [status, headers, body] - when nil - message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found." - [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]] - end - end - - class GzipStream - def initialize(body, mtime) - @body = body - @mtime = mtime - end - - def each(&block) - @writer = block - gzip =::Zlib::GzipWriter.new(self) - gzip.mtime = @mtime - @body.each { |part| gzip << part } - @body.close if @body.respond_to?(:close) - gzip.close - @writer = nil - end - - def write(data) - @writer.call(data) - end - end - - class DeflateStream - DEFLATE_ARGS = [ - Zlib::DEFAULT_COMPRESSION, - # drop the zlib header which causes both Safari and IE to choke - -Zlib::MAX_WBITS, - Zlib::DEF_MEM_LEVEL, - Zlib::DEFAULT_STRATEGY - ] - - def initialize(body) - @body = body - end - - def each - deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS) - @body.each { |part| yield deflater.deflate(part) } - @body.close if @body.respond_to?(:close) - yield deflater.finish - nil - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/directory.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/directory.rb deleted file mode 100644 index acdd302..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/directory.rb +++ /dev/null @@ -1,153 +0,0 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' - -module Rack - # Rack::Directory serves entries below the +root+ given, according to the - # path info of the Rack request. If a directory is found, the file's contents - # will be presented in an html based index. If a file is found, the env will - # be passed to the specified +app+. - # - # If +app+ is not specified, a Rack::File of the same +root+ will be used. - - class Directory - DIR_FILE = "%s%s%s%s" - DIR_PAGE = <<-PAGE - - %s - - - -

%s

-
- - - - - - - -%s -
NameSizeTypeLast Modified
-
- - PAGE - - attr_reader :files - attr_accessor :root, :path - - def initialize(root, app=nil) - @root = F.expand_path(root) - @app = app || Rack::File.new(@root) - end - - def call(env) - dup._call(env) - end - - F = ::File - - def _call(env) - @env = env - @script_name = env['SCRIPT_NAME'] - @path_info = Utils.unescape(env['PATH_INFO']) - - if forbidden = check_forbidden - forbidden - else - @path = F.join(@root, @path_info) - list_path - end - end - - def check_forbidden - return unless @path_info.include? ".." - - body = "Forbidden\n" - size = Rack::Utils.bytesize(body) - return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]] - end - - def list_directory - @files = [['../','Parent Directory','','','']] - glob = F.join(@path, '*') - - Dir[glob].sort.each do |node| - stat = stat(node) - next unless stat - basename = F.basename(node) - ext = F.extname(node) - - url = F.join(@script_name, @path_info, basename) - size = stat.size - type = stat.directory? ? 'directory' : Mime.mime_type(ext) - size = stat.directory? ? '-' : filesize_format(size) - mtime = stat.mtime.httpdate - url << '/' if stat.directory? - basename << '/' if stat.directory? - - @files << [ url, basename, size, type, mtime ] - end - - return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ] - end - - def stat(node, max = 10) - F.stat(node) - rescue Errno::ENOENT, Errno::ELOOP - return nil - end - - # TODO: add correct response if not readable, not sure if 404 is the best - # option - def list_path - @stat = F.stat(@path) - - if @stat.readable? - return @app.call(@env) if @stat.file? - return list_directory if @stat.directory? - else - raise Errno::ENOENT, 'No such file or directory' - end - - rescue Errno::ENOENT, Errno::ELOOP - return entity_not_found - end - - def entity_not_found - body = "Entity not found: #{@path_info}\n" - size = Rack::Utils.bytesize(body) - return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]] - end - - def each - show_path = @path.sub(/^#{@root}/,'') - files = @files.map{|f| DIR_FILE % f }*"\n" - page = DIR_PAGE % [ show_path, show_path , files ] - page.each_line{|l| yield l } - end - - # Stolen from Ramaze - - FILESIZE_FORMAT = [ - ['%.1fT', 1 << 40], - ['%.1fG', 1 << 30], - ['%.1fM', 1 << 20], - ['%.1fK', 1 << 10], - ] - - def filesize_format(int) - FILESIZE_FORMAT.each do |format, size| - return format % (int.to_f / size) if int >= size - end - - int.to_s + 'B' - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/file.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/file.rb deleted file mode 100644 index fe62bd6..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/file.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' - -module Rack - # Rack::File serves files below the +root+ given, according to the - # path info of the Rack request. - # - # Handlers can detect if bodies are a Rack::File, and use mechanisms - # like sendfile on the +path+. - - class File - attr_accessor :root - attr_accessor :path - - alias :to_path :path - - def initialize(root) - @root = root - end - - def call(env) - dup._call(env) - end - - F = ::File - - def _call(env) - @path_info = Utils.unescape(env["PATH_INFO"]) - return forbidden if @path_info.include? ".." - - @path = F.join(@root, @path_info) - - begin - if F.file?(@path) && F.readable?(@path) - serving - else - raise Errno::EPERM - end - rescue SystemCallError - not_found - end - end - - def forbidden - body = "Forbidden\n" - [403, {"Content-Type" => "text/plain", - "Content-Length" => body.size.to_s}, - [body]] - end - - # NOTE: - # We check via File::size? whether this file provides size info - # via stat (e.g. /proc files often don't), otherwise we have to - # figure it out by reading the whole file into memory. And while - # we're at it we also use this as body then. - - def serving - if size = F.size?(@path) - body = self - else - body = [F.read(@path)] - size = Utils.bytesize(body.first) - end - - [200, { - "Last-Modified" => F.mtime(@path).httpdate, - "Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain'), - "Content-Length" => size.to_s - }, body] - end - - def not_found - body = "File not found: #{@path_info}\n" - [404, {"Content-Type" => "text/plain", - "Content-Length" => body.size.to_s}, - [body]] - end - - def each - F.open(@path, "rb") { |file| - while part = file.read(8192) - yield part - end - } - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler.rb deleted file mode 100644 index 5624a1e..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler.rb +++ /dev/null @@ -1,69 +0,0 @@ -module Rack - # *Handlers* connect web servers with Rack. - # - # Rack includes Handlers for Mongrel, WEBrick, FastCGI, CGI, SCGI - # and LiteSpeed. - # - # Handlers usually are activated by calling MyHandler.run(myapp). - # A second optional hash can be passed to include server-specific - # configuration. - module Handler - def self.get(server) - return unless server - server = server.to_s - - if klass = @handlers[server] - obj = Object - klass.split("::").each { |x| obj = obj.const_get(x) } - obj - else - try_require('rack/handler', server) - const_get(server) - end - end - - # Transforms server-name constants to their canonical form as filenames, - # then tries to require them but silences the LoadError if not found - # - # Naming convention: - # - # Foo # => 'foo' - # FooBar # => 'foo_bar.rb' - # FooBAR # => 'foobar.rb' - # FOObar # => 'foobar.rb' - # FOOBAR # => 'foobar.rb' - # FooBarBaz # => 'foo_bar_baz.rb' - def self.try_require(prefix, const_name) - file = const_name.gsub(/^[A-Z]+/) { |pre| pre.downcase }. - gsub(/[A-Z]+[^A-Z]/, '_\&').downcase - - require(::File.join(prefix, file)) - rescue LoadError - end - - def self.register(server, klass) - @handlers ||= {} - @handlers[server] = klass - end - - autoload :CGI, "rack/handler/cgi" - autoload :FastCGI, "rack/handler/fastcgi" - autoload :Mongrel, "rack/handler/mongrel" - autoload :EventedMongrel, "rack/handler/evented_mongrel" - autoload :SwiftipliedMongrel, "rack/handler/swiftiplied_mongrel" - autoload :WEBrick, "rack/handler/webrick" - autoload :LSWS, "rack/handler/lsws" - autoload :SCGI, "rack/handler/scgi" - autoload :Thin, "rack/handler/thin" - - register 'cgi', 'Rack::Handler::CGI' - register 'fastcgi', 'Rack::Handler::FastCGI' - register 'mongrel', 'Rack::Handler::Mongrel' - register 'emongrel', 'Rack::Handler::EventedMongrel' - register 'smongrel', 'Rack::Handler::SwiftipliedMongrel' - register 'webrick', 'Rack::Handler::WEBrick' - register 'lsws', 'Rack::Handler::LSWS' - register 'scgi', 'Rack::Handler::SCGI' - register 'thin', 'Rack::Handler::Thin' - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/cgi.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/cgi.rb deleted file mode 100644 index f45f3d7..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/cgi.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'rack/content_length' - -module Rack - module Handler - class CGI - def self.run(app, options=nil) - serve app - end - - def self.serve(app) - app = ContentLength.new(app) - - env = ENV.to_hash - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - env.update({"rack.version" => [1,0], - "rack.input" => $stdin, - "rack.errors" => $stderr, - - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => true, - - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - - status, headers, body = app.call(env) - begin - send_headers status, headers - send_body body - ensure - body.close if body.respond_to? :close - end - end - - def self.send_headers(status, headers) - STDOUT.print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - STDOUT.print "#{k}: #{v}\r\n" - } - } - STDOUT.print "\r\n" - STDOUT.flush - end - - def self.send_body(body) - body.each { |part| - STDOUT.print part - STDOUT.flush - } - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb deleted file mode 100644 index 0f5cbf7..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/evented_mongrel.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'swiftcore/evented_mongrel' - -module Rack - module Handler - class EventedMongrel < Handler::Mongrel - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/fastcgi.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/fastcgi.rb deleted file mode 100644 index 2cbb502..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/fastcgi.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'fcgi' -require 'socket' -require 'rack/content_length' -require 'rack/rewindable_input' - -class FCGI::Stream - alias _rack_read_without_buffer read - - def read(n, buffer=nil) - buf = _rack_read_without_buffer n - buffer.replace(buf.to_s) if buffer - buf - end -end - -module Rack - module Handler - class FastCGI - def self.run(app, options={}) - file = options[:File] and STDIN.reopen(UNIXServer.new(file)) - port = options[:Port] and STDIN.reopen(TCPServer.new(port)) - FCGI.each { |request| - serve request, app - } - end - - def self.serve(request, app) - app = Rack::ContentLength.new(app) - - env = request.env - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - rack_input = RewindableInput.new(request.in) - - env.update({"rack.version" => [1,0], - "rack.input" => rack_input, - "rack.errors" => request.err, - - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" - }) - - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - env.delete "PATH_INFO" if env["PATH_INFO"] == "" - env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == "" - env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == "" - - begin - status, headers, body = app.call(env) - begin - send_headers request.out, status, headers - send_body request.out, body - ensure - body.close if body.respond_to? :close - end - ensure - rack_input.close - request.finish - end - end - - def self.send_headers(out, status, headers) - out.print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - out.print "#{k}: #{v}\r\n" - } - } - out.print "\r\n" - out.flush - end - - def self.send_body(out, body) - body.each { |part| - out.print part - out.flush - } - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/lsws.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/lsws.rb deleted file mode 100644 index 7231336..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/lsws.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'lsapi' -require 'rack/content_length' - -module Rack - module Handler - class LSWS - def self.run(app, options=nil) - while LSAPI.accept != nil - serve app - end - end - def self.serve(app) - app = Rack::ContentLength.new(app) - - env = ENV.to_hash - env.delete "HTTP_CONTENT_LENGTH" - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - env.update({"rack.version" => [1,0], - "rack.input" => StringIO.new($stdin.read.to_s), - "rack.errors" => $stderr, - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => false, - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - status, headers, body = app.call(env) - begin - send_headers status, headers - send_body body - ensure - body.close if body.respond_to? :close - end - end - def self.send_headers(status, headers) - print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - print "#{k}: #{v}\r\n" - } - } - print "\r\n" - STDOUT.flush - end - def self.send_body(body) - body.each { |part| - print part - STDOUT.flush - } - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/mongrel.rb deleted file mode 100644 index a6b4fa9..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/mongrel.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'mongrel' -require 'stringio' -require 'rack/content_length' -require 'rack/chunked' - -module Rack - module Handler - class Mongrel < ::Mongrel::HttpHandler - def self.run(app, options={}) - server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0', - options[:Port] || 8080) - # Acts like Rack::URLMap, utilizing Mongrel's own path finding methods. - # Use is similar to #run, replacing the app argument with a hash of - # { path=>app, ... } or an instance of Rack::URLMap. - if options[:map] - if app.is_a? Hash - app.each do |path, appl| - path = '/'+path unless path[0] == ?/ - server.register(path, Rack::Handler::Mongrel.new(appl)) - end - elsif app.is_a? URLMap - app.instance_variable_get(:@mapping).each do |(host, path, appl)| - next if !host.nil? && !options[:Host].nil? && options[:Host] != host - path = '/'+path unless path[0] == ?/ - server.register(path, Rack::Handler::Mongrel.new(appl)) - end - else - raise ArgumentError, "first argument should be a Hash or URLMap" - end - else - server.register('/', Rack::Handler::Mongrel.new(app)) - end - yield server if block_given? - server.run.join - end - - def initialize(app) - @app = Rack::Chunked.new(Rack::ContentLength.new(app)) - end - - def process(request, response) - env = {}.replace(request.params) - env.delete "HTTP_CONTENT_TYPE" - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - env.update({"rack.version" => [1,0], - "rack.input" => request.body || StringIO.new(""), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => false, # ??? - "rack.run_once" => false, - - "rack.url_scheme" => "http", - }) - env["QUERY_STRING"] ||= "" - env.delete "PATH_INFO" if env["PATH_INFO"] == "" - - status, headers, body = @app.call(env) - - begin - response.status = status.to_i - response.send_status(nil) - - headers.each { |k, vs| - vs.split("\n").each { |v| - response.header[k] = v - } - } - response.send_header - - body.each { |part| - response.write part - response.socket.flush - } - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/scgi.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/scgi.rb deleted file mode 100644 index df0e764..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/scgi.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'scgi' -require 'stringio' -require 'rack/content_length' -require 'rack/chunked' - -module Rack - module Handler - class SCGI < ::SCGI::Processor - attr_accessor :app - - def self.run(app, options=nil) - new(options.merge(:app=>app, - :host=>options[:Host], - :port=>options[:Port], - :socket=>options[:Socket])).listen - end - - def initialize(settings = {}) - @app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app])) - @log = Object.new - def @log.info(*args); end - def @log.error(*args); end - super(settings) - end - - def process_request(request, input_body, socket) - env = {}.replace(request) - env.delete "HTTP_CONTENT_TYPE" - env.delete "HTTP_CONTENT_LENGTH" - env["REQUEST_PATH"], env["QUERY_STRING"] = env["REQUEST_URI"].split('?', 2) - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["PATH_INFO"] = env["REQUEST_PATH"] - env["QUERY_STRING"] ||= "" - env["SCRIPT_NAME"] = "" - env.update({"rack.version" => [1,0], - "rack.input" => StringIO.new(input_body), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => true, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" - }) - status, headers, body = app.call(env) - begin - socket.write("Status: #{status}\r\n") - headers.each do |k, vs| - vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")} - end - socket.write("\r\n") - body.each {|s| socket.write(s)} - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb deleted file mode 100644 index 4bafd0b..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/swiftiplied_mongrel.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'swiftcore/swiftiplied_mongrel' - -module Rack - module Handler - class SwiftipliedMongrel < Handler::Mongrel - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/thin.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/thin.rb deleted file mode 100644 index 3d4fedf..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/thin.rb +++ /dev/null @@ -1,18 +0,0 @@ -require "thin" -require "rack/content_length" -require "rack/chunked" - -module Rack - module Handler - class Thin - def self.run(app, options={}) - app = Rack::Chunked.new(Rack::ContentLength.new(app)) - server = ::Thin::Server.new(options[:Host] || '0.0.0.0', - options[:Port] || 8080, - app) - yield server if block_given? - server.start - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/webrick.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/webrick.rb deleted file mode 100644 index 2bdc83a..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/handler/webrick.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'webrick' -require 'stringio' -require 'rack/content_length' - -module Rack - module Handler - class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet - def self.run(app, options={}) - server = ::WEBrick::HTTPServer.new(options) - server.mount "/", Rack::Handler::WEBrick, app - trap(:INT) { server.shutdown } - yield server if block_given? - server.start - end - - def initialize(server, app) - super server - @app = Rack::ContentLength.new(app) - end - - def service(req, res) - env = req.meta_vars - env.delete_if { |k, v| v.nil? } - - env.update({"rack.version" => [1,0], - "rack.input" => StringIO.new(req.body.to_s), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => false, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["QUERY_STRING"] ||= "" - env["REQUEST_PATH"] ||= "/" - if env["PATH_INFO"] == "" - env.delete "PATH_INFO" - else - path, n = req.request_uri.path, env["SCRIPT_NAME"].length - env["PATH_INFO"] = path[n, path.length-n] - end - - status, headers, body = @app.call(env) - begin - res.status = status.to_i - headers.each { |k, vs| - if k.downcase == "set-cookie" - res.cookies.concat vs.split("\n") - else - vs.split("\n").each { |v| - res[k] = v - } - end - } - body.each { |part| - res.body << part - } - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/head.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/head.rb deleted file mode 100644 index deab822..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/head.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Rack - -class Head - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - - if env["REQUEST_METHOD"] == "HEAD" - [status, headers, []] - else - [status, headers, body] - end - end -end - -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lint.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lint.rb deleted file mode 100644 index bb0693d..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lint.rb +++ /dev/null @@ -1,537 +0,0 @@ -require 'rack/utils' - -module Rack - # Rack::Lint validates your application and the requests and - # responses according to the Rack spec. - - class Lint - def initialize(app) - @app = app - end - - # :stopdoc: - - class LintError < RuntimeError; end - module Assertion - def assert(message, &block) - unless block.call - raise LintError, message - end - end - end - include Assertion - - ## This specification aims to formalize the Rack protocol. You - ## can (and should) use Rack::Lint to enforce it. - ## - ## When you develop middleware, be sure to add a Lint before and - ## after to catch all mistakes. - - ## = Rack applications - - ## A Rack application is an Ruby object (not a class) that - ## responds to +call+. - def call(env=nil) - dup._call(env) - end - - def _call(env) - ## It takes exactly one argument, the *environment* - assert("No env given") { env } - check_env env - - env['rack.input'] = InputWrapper.new(env['rack.input']) - env['rack.errors'] = ErrorWrapper.new(env['rack.errors']) - - ## and returns an Array of exactly three values: - status, headers, @body = @app.call(env) - ## The *status*, - check_status status - ## the *headers*, - check_headers headers - ## and the *body*. - check_content_type status, headers - check_content_length status, headers, env - [status, headers, self] - end - - ## == The Environment - def check_env(env) - ## The environment must be an true instance of Hash (no - ## subclassing allowed) that includes CGI-like headers. - ## The application is free to modify the environment. - assert("env #{env.inspect} is not a Hash, but #{env.class}") { - env.instance_of? Hash - } - - ## - ## The environment is required to include these variables - ## (adopted from PEP333), except when they'd be empty, but see - ## below. - - ## REQUEST_METHOD:: The HTTP request method, such as - ## "GET" or "POST". This cannot ever - ## be an empty string, and so is - ## always required. - - ## SCRIPT_NAME:: The initial portion of the request - ## URL's "path" that corresponds to the - ## application object, so that the - ## application knows its virtual - ## "location". This may be an empty - ## string, if the application corresponds - ## to the "root" of the server. - - ## PATH_INFO:: The remainder of the request URL's - ## "path", designating the virtual - ## "location" of the request's target - ## within the application. This may be an - ## empty string, if the request URL targets - ## the application root and does not have a - ## trailing slash. This value may be - ## percent-encoded when I originating from - ## a URL. - - ## QUERY_STRING:: The portion of the request URL that - ## follows the ?, if any. May be - ## empty, but is always required! - - ## SERVER_NAME, SERVER_PORT:: When combined with SCRIPT_NAME and PATH_INFO, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to SERVER_NAME for reconstructing the request URL. SERVER_NAME and SERVER_PORT can never be empty strings, and so are always required. - - ## HTTP_ Variables:: Variables corresponding to the - ## client-supplied HTTP request - ## headers (i.e., variables whose - ## names begin with HTTP_). The - ## presence or absence of these - ## variables should correspond with - ## the presence or absence of the - ## appropriate HTTP header in the - ## request. - - ## In addition to this, the Rack environment must include these - ## Rack-specific variables: - - ## rack.version:: The Array [1,0], representing this version of Rack. - ## rack.url_scheme:: +http+ or +https+, depending on the request URL. - ## rack.input:: See below, the input stream. - ## rack.errors:: See below, the error stream. - ## rack.multithread:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise. - ## rack.multiprocess:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise. - ## rack.run_once:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar). - ## - - ## Additional environment specifications have approved to - ## standardized middleware APIs. None of these are required to - ## be implemented by the server. - - ## rack.session:: A hash like interface for storing request session data. - ## The store must implement: - if session = env['rack.session'] - ## store(key, value) (aliased as []=); - assert("session #{session.inspect} must respond to store and []=") { - session.respond_to?(:store) && session.respond_to?(:[]=) - } - - ## fetch(key, default = nil) (aliased as []); - assert("session #{session.inspect} must respond to fetch and []") { - session.respond_to?(:fetch) && session.respond_to?(:[]) - } - - ## delete(key); - assert("session #{session.inspect} must respond to delete") { - session.respond_to?(:delete) - } - - ## clear; - assert("session #{session.inspect} must respond to clear") { - session.respond_to?(:clear) - } - end - - ## The server or the application can store their own data in the - ## environment, too. The keys must contain at least one dot, - ## and should be prefixed uniquely. The prefix rack. - ## is reserved for use with the Rack core distribution and other - ## accepted specifications and must not be used otherwise. - ## - - %w[REQUEST_METHOD SERVER_NAME SERVER_PORT - QUERY_STRING - rack.version rack.input rack.errors - rack.multithread rack.multiprocess rack.run_once].each { |header| - assert("env missing required key #{header}") { env.include? header } - } - - ## The environment must not contain the keys - ## HTTP_CONTENT_TYPE or HTTP_CONTENT_LENGTH - ## (use the versions without HTTP_). - %w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header| - assert("env contains #{header}, must use #{header[5,-1]}") { - not env.include? header - } - } - - ## The CGI keys (named without a period) must have String values. - env.each { |key, value| - next if key.include? "." # Skip extensions - assert("env variable #{key} has non-string value #{value.inspect}") { - value.instance_of? String - } - } - - ## - ## There are the following restrictions: - - ## * rack.version must be an array of Integers. - assert("rack.version must be an Array, was #{env["rack.version"].class}") { - env["rack.version"].instance_of? Array - } - ## * rack.url_scheme must either be +http+ or +https+. - assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") { - %w[http https].include? env["rack.url_scheme"] - } - - ## * There must be a valid input stream in rack.input. - check_input env["rack.input"] - ## * There must be a valid error stream in rack.errors. - check_error env["rack.errors"] - - ## * The REQUEST_METHOD must be a valid token. - assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") { - env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/ - } - - ## * The SCRIPT_NAME, if non-empty, must start with / - assert("SCRIPT_NAME must start with /") { - !env.include?("SCRIPT_NAME") || - env["SCRIPT_NAME"] == "" || - env["SCRIPT_NAME"] =~ /\A\// - } - ## * The PATH_INFO, if non-empty, must start with / - assert("PATH_INFO must start with /") { - !env.include?("PATH_INFO") || - env["PATH_INFO"] == "" || - env["PATH_INFO"] =~ /\A\// - } - ## * The CONTENT_LENGTH, if given, must consist of digits only. - assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") { - !env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/ - } - - ## * One of SCRIPT_NAME or PATH_INFO must be - ## set. PATH_INFO should be / if - ## SCRIPT_NAME is empty. - assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") { - env["SCRIPT_NAME"] || env["PATH_INFO"] - } - ## SCRIPT_NAME never should be /, but instead be empty. - assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") { - env["SCRIPT_NAME"] != "/" - } - end - - ## === The Input Stream - ## - ## The input stream is an IO-like object which contains the raw HTTP - ## POST data. If it is a file then it must be opened in binary mode. - def check_input(input) - ## The input stream must respond to +gets+, +each+, +read+ and +rewind+. - [:gets, :each, :read, :rewind].each { |method| - assert("rack.input #{input} does not respond to ##{method}") { - input.respond_to? method - } - } - end - - class InputWrapper - include Assertion - - def initialize(input) - @input = input - end - - def size - @input.size - end - - ## * +gets+ must be called without arguments and return a string, - ## or +nil+ on EOF. - def gets(*args) - assert("rack.input#gets called with arguments") { args.size == 0 } - v = @input.gets - assert("rack.input#gets didn't return a String") { - v.nil? or v.instance_of? String - } - v - end - - ## * +read+ behaves like IO#read. Its signature is read([length, [buffer]]). - ## If given, +length+ must be an non-negative Integer (>= 0) or +nil+, and +buffer+ must - ## be a String and may not be nil. If +length+ is given and not nil, then this method - ## reads at most +length+ bytes from the input stream. If +length+ is not given or nil, - ## then this method reads all data until EOF. - ## When EOF is reached, this method returns nil if +length+ is given and not nil, or "" - ## if +length+ is not given or is nil. - ## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a - ## newly created String object. - def read(*args) - assert("rack.input#read called with too many arguments") { - args.size <= 2 - } - if args.size >= 1 - assert("rack.input#read called with non-integer and non-nil length") { - args.first.kind_of?(Integer) || args.first.nil? - } - assert("rack.input#read called with a negative length") { - args.first.nil? || args.first >= 0 - } - end - if args.size >= 2 - assert("rack.input#read called with non-String buffer") { - args[1].kind_of?(String) - } - end - - v = @input.read(*args) - - assert("rack.input#read didn't return nil or a String") { - v.nil? or v.instance_of? String - } - if args[0].nil? - assert("rack.input#read(nil) returned nil on EOF") { - !v.nil? - } - end - - v - end - - ## * +each+ must be called without arguments and only yield Strings. - def each(*args) - assert("rack.input#each called with arguments") { args.size == 0 } - @input.each { |line| - assert("rack.input#each didn't yield a String") { - line.instance_of? String - } - yield line - } - end - - ## * +rewind+ must be called without arguments. It rewinds the input - ## stream back to the beginning. It must not raise Errno::ESPIPE: - ## that is, it may not be a pipe or a socket. Therefore, handler - ## developers must buffer the input data into some rewindable object - ## if the underlying input stream is not rewindable. - def rewind(*args) - assert("rack.input#rewind called with arguments") { args.size == 0 } - assert("rack.input#rewind raised Errno::ESPIPE") { - begin - @input.rewind - true - rescue Errno::ESPIPE - false - end - } - end - - ## * +close+ must never be called on the input stream. - def close(*args) - assert("rack.input#close must not be called") { false } - end - end - - ## === The Error Stream - def check_error(error) - ## The error stream must respond to +puts+, +write+ and +flush+. - [:puts, :write, :flush].each { |method| - assert("rack.error #{error} does not respond to ##{method}") { - error.respond_to? method - } - } - end - - class ErrorWrapper - include Assertion - - def initialize(error) - @error = error - end - - ## * +puts+ must be called with a single argument that responds to +to_s+. - def puts(str) - @error.puts str - end - - ## * +write+ must be called with a single argument that is a String. - def write(str) - assert("rack.errors#write not called with a String") { str.instance_of? String } - @error.write str - end - - ## * +flush+ must be called without arguments and must be called - ## in order to make the error appear for sure. - def flush - @error.flush - end - - ## * +close+ must never be called on the error stream. - def close(*args) - assert("rack.errors#close must not be called") { false } - end - end - - ## == The Response - - ## === The Status - def check_status(status) - ## This is an HTTP status. When parsed as integer (+to_i+), it must be - ## greater than or equal to 100. - assert("Status must be >=100 seen as integer") { status.to_i >= 100 } - end - - ## === The Headers - def check_headers(header) - ## The header must respond to +each+, and yield values of key and value. - assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") { - header.respond_to? :each - } - header.each { |key, value| - ## The header keys must be Strings. - assert("header key must be a string, was #{key.class}") { - key.instance_of? String - } - ## The header must not contain a +Status+ key, - assert("header must not contain Status") { key.downcase != "status" } - ## contain keys with : or newlines in their name, - assert("header names must not contain : or \\n") { key !~ /[:\n]/ } - ## contain keys names that end in - or _, - assert("header names must not end in - or _") { key !~ /[-_]\z/ } - ## but only contain keys that consist of - ## letters, digits, _ or - and start with a letter. - assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ } - - ## The values of the header must be Strings, - assert("a header value must be a String, but the value of " + - "'#{key}' is a #{value.class}") { value.kind_of? String } - ## consisting of lines (for multiple header values, e.g. multiple - ## Set-Cookie values) seperated by "\n". - value.split("\n").each { |item| - ## The lines must not contain characters below 037. - assert("invalid header value #{key}: #{item.inspect}") { - item !~ /[\000-\037]/ - } - } - } - end - - ## === The Content-Type - def check_content_type(status, headers) - headers.each { |key, value| - ## There must be a Content-Type, except when the - ## +Status+ is 1xx, 204 or 304, in which case there must be none - ## given. - if key.downcase == "content-type" - assert("Content-Type header found in #{status} response, not allowed") { - not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - return - end - } - assert("No Content-Type header found") { - Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - end - - ## === The Content-Length - def check_content_length(status, headers, env) - headers.each { |key, value| - if key.downcase == 'content-length' - ## There must not be a Content-Length header when the - ## +Status+ is 1xx, 204 or 304. - assert("Content-Length header found in #{status} response, not allowed") { - not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - - bytes = 0 - string_body = true - - if @body.respond_to?(:to_ary) - @body.each { |part| - unless part.kind_of?(String) - string_body = false - break - end - - bytes += Rack::Utils.bytesize(part) - } - - if env["REQUEST_METHOD"] == "HEAD" - assert("Response body was given for HEAD request, but should be empty") { - bytes == 0 - } - else - if string_body - assert("Content-Length header was #{value}, but should be #{bytes}") { - value == bytes.to_s - } - end - end - end - - return - end - } - end - - ## === The Body - def each - @closed = false - ## The Body must respond to +each+ - @body.each { |part| - ## and must only yield String values. - assert("Body yielded non-string value #{part.inspect}") { - part.instance_of? String - } - yield part - } - ## - ## The Body itself should not be an instance of String, as this will - ## break in Ruby 1.9. - ## - ## If the Body responds to +close+, it will be called after iteration. - # XXX howto: assert("Body has not been closed") { @closed } - - - ## - ## If the Body responds to +to_path+, it must return a String - ## identifying the location of a file whose contents are identical - ## to that produced by calling +each+; this may be used by the - ## server as an alternative, possibly more efficient way to - ## transport the response. - - if @body.respond_to?(:to_path) - assert("The file identified by body.to_path does not exist") { - ::File.exist? @body.to_path - } - end - - ## - ## The Body commonly is an Array of Strings, the application - ## instance itself, or a File-like object. - end - - def close - @closed = true - @body.close if @body.respond_to?(:close) - end - - # :startdoc: - - end -end - -## == Thanks -## Some parts of this specification are adopted from PEP333: Python -## Web Server Gateway Interface -## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank -## everyone involved in that effort. diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lobster.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lobster.rb deleted file mode 100644 index f63f419..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lobster.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'zlib' - -require 'rack/request' -require 'rack/response' - -module Rack - # Paste has a Pony, Rack has a Lobster! - class Lobster - LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2 - P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0 - t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ - I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0]) - - LambdaLobster = lambda { |env| - if env["QUERY_STRING"].include?("flip") - lobster = LobsterString.split("\n"). - map { |line| line.ljust(42).reverse }. - join("\n") - href = "?" - else - lobster = LobsterString - href = "?flip" - end - - content = ["Lobstericious!", - "
", lobster, "
", - "flip!"] - length = content.inject(0) { |a,e| a+e.size }.to_s - [200, {"Content-Type" => "text/html", "Content-Length" => length}, content] - } - - def call(env) - req = Request.new(env) - if req.GET["flip"] == "left" - lobster = LobsterString.split("\n"). - map { |line| line.ljust(42).reverse }. - join("\n") - href = "?flip=right" - elsif req.GET["flip"] == "crash" - raise "Lobster crashed" - else - lobster = LobsterString - href = "?flip=left" - end - - res = Response.new - res.write "Lobstericious!" - res.write "
"
-      res.write lobster
-      res.write "
" - res.write "

flip!

" - res.write "

crash!

" - res.finish - end - - end -end - -if $0 == __FILE__ - require 'rack' - require 'rack/showexceptions' - Rack::Handler::WEBrick.run \ - Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), - :Port => 9292 -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lock.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lock.rb deleted file mode 100644 index 9323852..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/lock.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Rack - class Lock - FLAG = 'rack.multithread'.freeze - - def initialize(app, lock = Mutex.new) - @app, @lock = app, lock - end - - def call(env) - old, env[FLAG] = env[FLAG], false - @lock.synchronize { @app.call(env) } - ensure - env[FLAG] = old - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/methodoverride.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/methodoverride.rb deleted file mode 100644 index 0eed29f..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/methodoverride.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Rack - class MethodOverride - HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS) - - METHOD_OVERRIDE_PARAM_KEY = "_method".freeze - HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze - - def initialize(app) - @app = app - end - - def call(env) - if env["REQUEST_METHOD"] == "POST" - req = Request.new(env) - method = req.POST[METHOD_OVERRIDE_PARAM_KEY] || - env[HTTP_METHOD_OVERRIDE_HEADER] - method = method.to_s.upcase - if HTTP_METHODS.include?(method) - env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"] - env["REQUEST_METHOD"] = method - end - end - - @app.call(env) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mime.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mime.rb deleted file mode 100644 index 5a6a73a..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mime.rb +++ /dev/null @@ -1,204 +0,0 @@ -module Rack - module Mime - # Returns String with mime type if found, otherwise use +fallback+. - # +ext+ should be filename extension in the '.ext' format that - # File.extname(file) returns. - # +fallback+ may be any object - # - # Also see the documentation for MIME_TYPES - # - # Usage: - # Rack::Mime.mime_type('.foo') - # - # This is a shortcut for: - # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream') - - def mime_type(ext, fallback='application/octet-stream') - MIME_TYPES.fetch(ext, fallback) - end - module_function :mime_type - - # List of most common mime-types, selected various sources - # according to their usefulness in a webserving scope for Ruby - # users. - # - # To amend this list with your local mime.types list you can use: - # - # require 'webrick/httputils' - # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types') - # Rack::Mime::MIME_TYPES.merge!(list) - # - # To add the list mongrel provides, use: - # - # require 'mongrel/handlers' - # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES) - - MIME_TYPES = { - ".3gp" => "video/3gpp", - ".a" => "application/octet-stream", - ".ai" => "application/postscript", - ".aif" => "audio/x-aiff", - ".aiff" => "audio/x-aiff", - ".asc" => "application/pgp-signature", - ".asf" => "video/x-ms-asf", - ".asm" => "text/x-asm", - ".asx" => "video/x-ms-asf", - ".atom" => "application/atom+xml", - ".au" => "audio/basic", - ".avi" => "video/x-msvideo", - ".bat" => "application/x-msdownload", - ".bin" => "application/octet-stream", - ".bmp" => "image/bmp", - ".bz2" => "application/x-bzip2", - ".c" => "text/x-c", - ".cab" => "application/vnd.ms-cab-compressed", - ".cc" => "text/x-c", - ".chm" => "application/vnd.ms-htmlhelp", - ".class" => "application/octet-stream", - ".com" => "application/x-msdownload", - ".conf" => "text/plain", - ".cpp" => "text/x-c", - ".crt" => "application/x-x509-ca-cert", - ".css" => "text/css", - ".csv" => "text/csv", - ".cxx" => "text/x-c", - ".deb" => "application/x-debian-package", - ".der" => "application/x-x509-ca-cert", - ".diff" => "text/x-diff", - ".djv" => "image/vnd.djvu", - ".djvu" => "image/vnd.djvu", - ".dll" => "application/x-msdownload", - ".dmg" => "application/octet-stream", - ".doc" => "application/msword", - ".dot" => "application/msword", - ".dtd" => "application/xml-dtd", - ".dvi" => "application/x-dvi", - ".ear" => "application/java-archive", - ".eml" => "message/rfc822", - ".eps" => "application/postscript", - ".exe" => "application/x-msdownload", - ".f" => "text/x-fortran", - ".f77" => "text/x-fortran", - ".f90" => "text/x-fortran", - ".flv" => "video/x-flv", - ".for" => "text/x-fortran", - ".gem" => "application/octet-stream", - ".gemspec" => "text/x-script.ruby", - ".gif" => "image/gif", - ".gz" => "application/x-gzip", - ".h" => "text/x-c", - ".hh" => "text/x-c", - ".htm" => "text/html", - ".html" => "text/html", - ".ico" => "image/vnd.microsoft.icon", - ".ics" => "text/calendar", - ".ifb" => "text/calendar", - ".iso" => "application/octet-stream", - ".jar" => "application/java-archive", - ".java" => "text/x-java-source", - ".jnlp" => "application/x-java-jnlp-file", - ".jpeg" => "image/jpeg", - ".jpg" => "image/jpeg", - ".js" => "application/javascript", - ".json" => "application/json", - ".log" => "text/plain", - ".m3u" => "audio/x-mpegurl", - ".m4v" => "video/mp4", - ".man" => "text/troff", - ".mathml" => "application/mathml+xml", - ".mbox" => "application/mbox", - ".mdoc" => "text/troff", - ".me" => "text/troff", - ".mid" => "audio/midi", - ".midi" => "audio/midi", - ".mime" => "message/rfc822", - ".mml" => "application/mathml+xml", - ".mng" => "video/x-mng", - ".mov" => "video/quicktime", - ".mp3" => "audio/mpeg", - ".mp4" => "video/mp4", - ".mp4v" => "video/mp4", - ".mpeg" => "video/mpeg", - ".mpg" => "video/mpeg", - ".ms" => "text/troff", - ".msi" => "application/x-msdownload", - ".odp" => "application/vnd.oasis.opendocument.presentation", - ".ods" => "application/vnd.oasis.opendocument.spreadsheet", - ".odt" => "application/vnd.oasis.opendocument.text", - ".ogg" => "application/ogg", - ".p" => "text/x-pascal", - ".pas" => "text/x-pascal", - ".pbm" => "image/x-portable-bitmap", - ".pdf" => "application/pdf", - ".pem" => "application/x-x509-ca-cert", - ".pgm" => "image/x-portable-graymap", - ".pgp" => "application/pgp-encrypted", - ".pkg" => "application/octet-stream", - ".pl" => "text/x-script.perl", - ".pm" => "text/x-script.perl-module", - ".png" => "image/png", - ".pnm" => "image/x-portable-anymap", - ".ppm" => "image/x-portable-pixmap", - ".pps" => "application/vnd.ms-powerpoint", - ".ppt" => "application/vnd.ms-powerpoint", - ".ps" => "application/postscript", - ".psd" => "image/vnd.adobe.photoshop", - ".py" => "text/x-script.python", - ".qt" => "video/quicktime", - ".ra" => "audio/x-pn-realaudio", - ".rake" => "text/x-script.ruby", - ".ram" => "audio/x-pn-realaudio", - ".rar" => "application/x-rar-compressed", - ".rb" => "text/x-script.ruby", - ".rdf" => "application/rdf+xml", - ".roff" => "text/troff", - ".rpm" => "application/x-redhat-package-manager", - ".rss" => "application/rss+xml", - ".rtf" => "application/rtf", - ".ru" => "text/x-script.ruby", - ".s" => "text/x-asm", - ".sgm" => "text/sgml", - ".sgml" => "text/sgml", - ".sh" => "application/x-sh", - ".sig" => "application/pgp-signature", - ".snd" => "audio/basic", - ".so" => "application/octet-stream", - ".svg" => "image/svg+xml", - ".svgz" => "image/svg+xml", - ".swf" => "application/x-shockwave-flash", - ".t" => "text/troff", - ".tar" => "application/x-tar", - ".tbz" => "application/x-bzip-compressed-tar", - ".tcl" => "application/x-tcl", - ".tex" => "application/x-tex", - ".texi" => "application/x-texinfo", - ".texinfo" => "application/x-texinfo", - ".text" => "text/plain", - ".tif" => "image/tiff", - ".tiff" => "image/tiff", - ".torrent" => "application/x-bittorrent", - ".tr" => "text/troff", - ".txt" => "text/plain", - ".vcf" => "text/x-vcard", - ".vcs" => "text/x-vcalendar", - ".vrml" => "model/vrml", - ".war" => "application/java-archive", - ".wav" => "audio/x-wav", - ".wma" => "audio/x-ms-wma", - ".wmv" => "video/x-ms-wmv", - ".wmx" => "video/x-ms-wmx", - ".wrl" => "model/vrml", - ".wsdl" => "application/wsdl+xml", - ".xbm" => "image/x-xbitmap", - ".xhtml" => "application/xhtml+xml", - ".xls" => "application/vnd.ms-excel", - ".xml" => "application/xml", - ".xpm" => "image/x-xpixmap", - ".xsl" => "application/xml", - ".xslt" => "application/xslt+xml", - ".yaml" => "text/yaml", - ".yml" => "text/yaml", - ".zip" => "application/zip", - } - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mock.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mock.rb deleted file mode 100644 index fdefb03..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/mock.rb +++ /dev/null @@ -1,184 +0,0 @@ -require 'uri' -require 'stringio' -require 'rack/lint' -require 'rack/utils' -require 'rack/response' - -module Rack - # Rack::MockRequest helps testing your Rack application without - # actually using HTTP. - # - # After performing a request on a URL with get/post/put/delete, it - # returns a MockResponse with useful helper methods for effective - # testing. - # - # You can pass a hash with additional configuration to the - # get/post/put/delete. - # :input:: A String or IO-like to be used as rack.input. - # :fatal:: Raise a FatalWarning if the app writes to rack.errors. - # :lint:: If true, wrap the application in a Rack::Lint. - - class MockRequest - class FatalWarning < RuntimeError - end - - class FatalWarner - def puts(warning) - raise FatalWarning, warning - end - - def write(warning) - raise FatalWarning, warning - end - - def flush - end - - def string - "" - end - end - - DEFAULT_ENV = { - "rack.version" => [1,0], - "rack.input" => StringIO.new, - "rack.errors" => StringIO.new, - "rack.multithread" => true, - "rack.multiprocess" => true, - "rack.run_once" => false, - } - - def initialize(app) - @app = app - end - - def get(uri, opts={}) request("GET", uri, opts) end - def post(uri, opts={}) request("POST", uri, opts) end - def put(uri, opts={}) request("PUT", uri, opts) end - def delete(uri, opts={}) request("DELETE", uri, opts) end - - def request(method="GET", uri="", opts={}) - env = self.class.env_for(uri, opts.merge(:method => method)) - - if opts[:lint] - app = Rack::Lint.new(@app) - else - app = @app - end - - errors = env["rack.errors"] - MockResponse.new(*(app.call(env) + [errors])) - end - - # Return the Rack environment used for a request to +uri+. - def self.env_for(uri="", opts={}) - uri = URI(uri) - uri.path = "/#{uri.path}" unless uri.path[0] == ?/ - - env = DEFAULT_ENV.dup - - env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET" - env["SERVER_NAME"] = uri.host || "example.org" - env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80" - env["QUERY_STRING"] = uri.query.to_s - env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path - env["rack.url_scheme"] = uri.scheme || "http" - env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off" - - env["SCRIPT_NAME"] = opts[:script_name] || "" - - if opts[:fatal] - env["rack.errors"] = FatalWarner.new - else - env["rack.errors"] = StringIO.new - end - - if params = opts[:params] - if env["REQUEST_METHOD"] == "GET" - params = Utils.parse_nested_query(params) if params.is_a?(String) - params.update(Utils.parse_nested_query(env["QUERY_STRING"])) - env["QUERY_STRING"] = Utils.build_nested_query(params) - elsif !opts.has_key?(:input) - opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded" - if params.is_a?(Hash) - if data = Utils::Multipart.build_multipart(params) - opts[:input] = data - opts["CONTENT_LENGTH"] ||= data.length.to_s - opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}" - else - opts[:input] = Utils.build_nested_query(params) - end - else - opts[:input] = params - end - end - end - - opts[:input] ||= "" - if String === opts[:input] - env["rack.input"] = StringIO.new(opts[:input]) - else - env["rack.input"] = opts[:input] - end - - env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s - - opts.each { |field, value| - env[field] = value if String === field - } - - env - end - end - - # Rack::MockResponse provides useful helpers for testing your apps. - # Usually, you don't create the MockResponse on your own, but use - # MockRequest. - - class MockResponse - def initialize(status, headers, body, errors=StringIO.new("")) - @status = status.to_i - - @original_headers = headers - @headers = Rack::Utils::HeaderHash.new - headers.each { |field, values| - @headers[field] = values - @headers[field] = "" if values.empty? - } - - @body = "" - body.each { |part| @body << part } - - @errors = errors.string if errors.respond_to?(:string) - end - - # Status - attr_reader :status - - # Headers - attr_reader :headers, :original_headers - - def [](field) - headers[field] - end - - - # Body - attr_reader :body - - def =~(other) - @body =~ other - end - - def match(other) - @body.match other - end - - - # Errors - attr_accessor :errors - - - include Response::Helpers - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/recursive.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/recursive.rb deleted file mode 100644 index bf8b965..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/recursive.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'uri' - -module Rack - # Rack::ForwardRequest gets caught by Rack::Recursive and redirects - # the current request to the app at +url+. - # - # raise ForwardRequest.new("/not-found") - # - - class ForwardRequest < Exception - attr_reader :url, :env - - def initialize(url, env={}) - @url = URI(url) - @env = env - - @env["PATH_INFO"] = @url.path - @env["QUERY_STRING"] = @url.query if @url.query - @env["HTTP_HOST"] = @url.host if @url.host - @env["HTTP_PORT"] = @url.port if @url.port - @env["rack.url_scheme"] = @url.scheme if @url.scheme - - super "forwarding to #{url}" - end - end - - # Rack::Recursive allows applications called down the chain to - # include data from other applications (by using - # rack['rack.recursive.include'][...] or raise a - # ForwardRequest to redirect internally. - - class Recursive - def initialize(app) - @app = app - end - - def call(env) - @script_name = env["SCRIPT_NAME"] - @app.call(env.merge('rack.recursive.include' => method(:include))) - rescue ForwardRequest => req - call(env.merge(req.env)) - end - - def include(env, path) - unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ || - path[@script_name.size].nil?) - raise ArgumentError, "can only include below #{@script_name}, not #{path}" - end - - env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name, - "REQUEST_METHOD" => "GET", - "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "", - "rack.input" => StringIO.new("")) - @app.call(env) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/reloader.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/reloader.rb deleted file mode 100644 index aa2f060..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/reloader.rb +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com -# All files in this distribution are subject to the terms of the Ruby license. - -require 'pathname' - -module Rack - - # High performant source reloader - # - # This class acts as Rack middleware. - # - # What makes it especially suited for use in a production environment is that - # any file will only be checked once and there will only be made one system - # call stat(2). - # - # Please note that this will not reload files in the background, it does so - # only when actively called. - # - # It is performing a check/reload cycle at the start of every request, but - # also respects a cool down time, during which nothing will be done. - class Reloader - def initialize(app, cooldown = 10, backend = Stat) - @app = app - @cooldown = cooldown - @last = (Time.now - cooldown) - @cache = {} - @mtimes = {} - - extend backend - end - - def call(env) - if @cooldown and Time.now > @last + @cooldown - if Thread.list.size > 1 - Thread.exclusive{ reload! } - else - reload! - end - - @last = Time.now - end - - @app.call(env) - end - - def reload!(stderr = $stderr) - rotation do |file, mtime| - previous_mtime = @mtimes[file] ||= mtime - safe_load(file, mtime, stderr) if mtime > previous_mtime - end - end - - # A safe Kernel::load, issuing the hooks depending on the results - def safe_load(file, mtime, stderr = $stderr) - load(file) - stderr.puts "#{self.class}: reloaded `#{file}'" - file - rescue LoadError, SyntaxError => ex - stderr.puts ex - ensure - @mtimes[file] = mtime - end - - module Stat - def rotation - files = [$0, *$LOADED_FEATURES].uniq - paths = ['./', *$LOAD_PATH].uniq - - files.map{|file| - next if file =~ /\.(so|bundle)$/ # cannot reload compiled files - - found, stat = figure_path(file, paths) - next unless found and stat and mtime = stat.mtime - - @cache[file] = found - - yield(found, mtime) - }.compact - end - - # Takes a relative or absolute +file+ name, a couple possible +paths+ that - # the +file+ might reside in. Returns the full path and File::Stat for the - # path. - def figure_path(file, paths) - found = @cache[file] - found = file if !found and Pathname.new(file).absolute? - found, stat = safe_stat(found) - return found, stat if found - - paths.each do |possible_path| - path = ::File.join(possible_path, file) - found, stat = safe_stat(path) - return ::File.expand_path(found), stat if found - end - end - - def safe_stat(file) - return unless file - stat = ::File.stat(file) - return file, stat if stat.file? - rescue Errno::ENOENT, Errno::ENOTDIR - @cache.delete(file) and false - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/request.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/request.rb deleted file mode 100644 index 04cdde0..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/request.rb +++ /dev/null @@ -1,254 +0,0 @@ -require 'rack/utils' - -module Rack - # Rack::Request provides a convenient interface to a Rack - # environment. It is stateless, the environment +env+ passed to the - # constructor will be directly modified. - # - # req = Rack::Request.new(env) - # req.post? - # req.params["data"] - # - # The environment hash passed will store a reference to the Request object - # instantiated so that it will only instantiate if an instance of the Request - # object doesn't already exist. - - class Request - # The environment of the request. - attr_reader :env - - def self.new(env, *args) - if self == Rack::Request - env["rack.request"] ||= super - else - super - end - end - - def initialize(env) - @env = env - end - - def body; @env["rack.input"] end - def scheme; @env["rack.url_scheme"] end - def script_name; @env["SCRIPT_NAME"].to_s end - def path_info; @env["PATH_INFO"].to_s end - def port; @env["SERVER_PORT"].to_i end - def request_method; @env["REQUEST_METHOD"] end - def query_string; @env["QUERY_STRING"].to_s end - def content_length; @env['CONTENT_LENGTH'] end - def content_type; @env['CONTENT_TYPE'] end - def session; @env['rack.session'] ||= {} end - def session_options; @env['rack.session.options'] ||= {} end - - # The media type (type/subtype) portion of the CONTENT_TYPE header - # without any media type parameters. e.g., when CONTENT_TYPE is - # "text/plain;charset=utf-8", the media-type is "text/plain". - # - # For more information on the use of media types in HTTP, see: - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 - def media_type - content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase - end - - # The media type parameters provided in CONTENT_TYPE as a Hash, or - # an empty Hash if no CONTENT_TYPE or media-type parameters were - # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8", - # this method responds with the following Hash: - # { 'charset' => 'utf-8' } - def media_type_params - return {} if content_type.nil? - content_type.split(/\s*[;,]\s*/)[1..-1]. - collect { |s| s.split('=', 2) }. - inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash } - end - - # The character set of the request body if a "charset" media type - # parameter was given, or nil if no "charset" was specified. Note - # that, per RFC2616, text/* media types that specify no explicit - # charset are to be considered ISO-8859-1. - def content_charset - media_type_params['charset'] - end - - def host - # Remove port number. - (@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '') - end - - def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end - def path_info=(s); @env["PATH_INFO"] = s.to_s end - - def get?; request_method == "GET" end - def post?; request_method == "POST" end - def put?; request_method == "PUT" end - def delete?; request_method == "DELETE" end - def head?; request_method == "HEAD" end - - # The set of form-data media-types. Requests that do not indicate - # one of the media types presents in this list will not be eligible - # for form-data / param parsing. - FORM_DATA_MEDIA_TYPES = [ - nil, - 'application/x-www-form-urlencoded', - 'multipart/form-data' - ] - - # The set of media-types. Requests that do not indicate - # one of the media types presents in this list will not be eligible - # for param parsing like soap attachments or generic multiparts - PARSEABLE_DATA_MEDIA_TYPES = [ - 'multipart/related', - 'multipart/mixed' - ] - - # Determine whether the request body contains form-data by checking - # the request media_type against registered form-data media-types: - # "application/x-www-form-urlencoded" and "multipart/form-data". The - # list of form-data media types can be modified through the - # +FORM_DATA_MEDIA_TYPES+ array. - def form_data? - FORM_DATA_MEDIA_TYPES.include?(media_type) - end - - # Determine whether the request body contains data by checking - # the request media_type against registered parse-data media-types - def parseable_data? - PARSEABLE_DATA_MEDIA_TYPES.include?(media_type) - end - - # Returns the data recieved in the query string. - def GET - if @env["rack.request.query_string"] == query_string - @env["rack.request.query_hash"] - else - @env["rack.request.query_string"] = query_string - @env["rack.request.query_hash"] = - Utils.parse_nested_query(query_string) - end - end - - # Returns the data recieved in the request body. - # - # This method support both application/x-www-form-urlencoded and - # multipart/form-data. - def POST - if @env["rack.request.form_input"].eql? @env["rack.input"] - @env["rack.request.form_hash"] - elsif form_data? || parseable_data? - @env["rack.request.form_input"] = @env["rack.input"] - unless @env["rack.request.form_hash"] = - Utils::Multipart.parse_multipart(env) - form_vars = @env["rack.input"].read - - # Fix for Safari Ajax postings that always append \0 - form_vars.sub!(/\0\z/, '') - - @env["rack.request.form_vars"] = form_vars - @env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars) - - @env["rack.input"].rewind - end - @env["rack.request.form_hash"] - else - {} - end - end - - # The union of GET and POST data. - def params - self.put? ? self.GET : self.GET.update(self.POST) - rescue EOFError => e - self.GET - end - - # shortcut for request.params[key] - def [](key) - params[key.to_s] - end - - # shortcut for request.params[key] = value - def []=(key, value) - params[key.to_s] = value - end - - # like Hash#values_at - def values_at(*keys) - keys.map{|key| params[key] } - end - - # the referer of the client or '/' - def referer - @env['HTTP_REFERER'] || '/' - end - alias referrer referer - - - def cookies - return {} unless @env["HTTP_COOKIE"] - - if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] - @env["rack.request.cookie_hash"] - else - @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] - # According to RFC 2109: - # If multiple cookies satisfy the criteria above, they are ordered in - # the Cookie header such that those with more specific Path attributes - # precede those with less specific. Ordering with respect to other - # attributes (e.g., Domain) is unspecified. - @env["rack.request.cookie_hash"] = - Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)| - h[k] = Array === v ? v.first : v - h - } - end - end - - def xhr? - @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" - end - - # Tries to return a remake of the original request URL as a string. - def url - url = scheme + "://" - url << host - - if scheme == "https" && port != 443 || - scheme == "http" && port != 80 - url << ":#{port}" - end - - url << fullpath - - url - end - - def path - script_name + path_info - end - - def fullpath - query_string.empty? ? path : "#{path}?#{query_string}" - end - - def accept_encoding - @env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part| - m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick - - if m - [m[1], (m[2] || 1.0).to_f] - else - raise "Invalid value for Accept-Encoding: #{part.inspect}" - end - end - end - - def ip - if addr = @env['HTTP_X_FORWARDED_FOR'] - addr.split(',').last.strip - else - @env['REMOTE_ADDR'] - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/response.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/response.rb deleted file mode 100644 index 28b4d83..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/response.rb +++ /dev/null @@ -1,183 +0,0 @@ -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::Response provides a convenient interface to create a Rack - # response. - # - # It allows setting of headers and cookies, and provides useful - # defaults (a OK response containing HTML). - # - # You can use Response#write to iteratively generate your response, - # but note that this is buffered by Rack::Response until you call - # +finish+. +finish+ however can take a block inside which calls to - # +write+ are syncronous with the Rack response. - # - # Your application's +call+ should end returning Response#finish. - - class Response - attr_accessor :length - - def initialize(body=[], status=200, header={}, &block) - @status = status - @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}. - merge(header)) - - @writer = lambda { |x| @body << x } - @block = nil - @length = 0 - - @body = [] - - if body.respond_to? :to_str - write body.to_str - elsif body.respond_to?(:each) - body.each { |part| - write part.to_s - } - else - raise TypeError, "stringable or iterable required" - end - - yield self if block_given? - end - - attr_reader :header - attr_accessor :status, :body - - def [](key) - header[key] - end - - def []=(key, value) - header[key] = value - end - - def set_cookie(key, value) - case value - when Hash - domain = "; domain=" + value[:domain] if value[:domain] - path = "; path=" + value[:path] if value[:path] - # According to RFC 2109, we need dashes here. - # N.B.: cgi.rb uses spaces... - expires = "; expires=" + value[:expires].clone.gmtime. - strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires] - secure = "; secure" if value[:secure] - httponly = "; HttpOnly" if value[:httponly] - value = value[:value] - end - value = [value] unless Array === value - cookie = Utils.escape(key) + "=" + - value.map { |v| Utils.escape v }.join("&") + - "#{domain}#{path}#{expires}#{secure}#{httponly}" - - case self["Set-Cookie"] - when Array - self["Set-Cookie"] << cookie - when String - self["Set-Cookie"] = [self["Set-Cookie"], cookie] - when nil - self["Set-Cookie"] = cookie - end - end - - def delete_cookie(key, value={}) - unless Array === self["Set-Cookie"] - self["Set-Cookie"] = [self["Set-Cookie"]].compact - end - - self["Set-Cookie"].reject! { |cookie| - cookie =~ /\A#{Utils.escape(key)}=/ - } - - set_cookie(key, - {:value => '', :path => nil, :domain => nil, - :expires => Time.at(0) }.merge(value)) - end - - def redirect(target, status=302) - self.status = status - self["Location"] = target - end - - def finish(&block) - @block = block - - if [204, 304].include?(status.to_i) - header.delete "Content-Type" - [status.to_i, header.to_hash, []] - else - [status.to_i, header.to_hash, self] - end - end - alias to_a finish # For *response - - def each(&callback) - @body.each(&callback) - @writer = callback - @block.call(self) if @block - end - - # Append to body and update Content-Length. - # - # NOTE: Do not mix #write and direct #body access! - # - def write(str) - s = str.to_s - @length += Rack::Utils.bytesize(s) - @writer.call s - - header["Content-Length"] = @length.to_s - str - end - - def close - body.close if body.respond_to?(:close) - end - - def empty? - @block == nil && @body.empty? - end - - alias headers header - - module Helpers - def invalid?; @status < 100 || @status >= 600; end - - def informational?; @status >= 100 && @status < 200; end - def successful?; @status >= 200 && @status < 300; end - def redirection?; @status >= 300 && @status < 400; end - def client_error?; @status >= 400 && @status < 500; end - def server_error?; @status >= 500 && @status < 600; end - - def ok?; @status == 200; end - def forbidden?; @status == 403; end - def not_found?; @status == 404; end - - def redirect?; [301, 302, 303, 307].include? @status; end - def empty?; [201, 204, 304].include? @status; end - - # Headers - attr_reader :headers, :original_headers - - def include?(header) - !!headers[header] - end - - def content_type - headers["Content-Type"] - end - - def content_length - cl = headers["Content-Length"] - cl ? cl.to_i : cl - end - - def location - headers["Location"] - end - end - - include Helpers - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/rewindable_input.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/rewindable_input.rb deleted file mode 100644 index 2347dc3..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/rewindable_input.rb +++ /dev/null @@ -1,98 +0,0 @@ -require 'tempfile' - -module Rack - # Class which can make any IO object rewindable, including non-rewindable ones. It does - # this by buffering the data into a tempfile, which is rewindable. - # - # rack.input is required to be rewindable, so if your input stream IO is non-rewindable - # by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class - # to easily make it rewindable. - # - # Don't forget to call #close when you're done. This frees up temporary resources that - # RewindableInput uses, though it does *not* close the original IO object. - class RewindableInput - def initialize(io) - @io = io - @rewindable_io = nil - @unlinked = false - end - - def gets - make_rewindable unless @rewindable_io - @rewindable_io.gets - end - - def read(*args) - make_rewindable unless @rewindable_io - @rewindable_io.read(*args) - end - - def each(&block) - make_rewindable unless @rewindable_io - @rewindable_io.each(&block) - end - - def rewind - make_rewindable unless @rewindable_io - @rewindable_io.rewind - end - - # Closes this RewindableInput object without closing the originally - # wrapped IO oject. Cleans up any temporary resources that this RewindableInput - # has created. - # - # This method may be called multiple times. It does nothing on subsequent calls. - def close - if @rewindable_io - if @unlinked - @rewindable_io.close - else - @rewindable_io.close! - end - @rewindable_io = nil - end - end - - private - - # Ruby's Tempfile class has a bug. Subclass it and fix it. - class Tempfile < ::Tempfile - def _close - @tmpfile.close if @tmpfile - @data[1] = nil if @data - @tmpfile = nil - end - end - - def make_rewindable - # Buffer all data into a tempfile. Since this tempfile is private to this - # RewindableInput object, we chmod it so that nobody else can read or write - # it. On POSIX filesystems we also unlink the file so that it doesn't - # even have a file entry on the filesystem anymore, though we can still - # access it because we have the file handle open. - @rewindable_io = Tempfile.new('RackRewindableInput') - @rewindable_io.chmod(0000) - if filesystem_has_posix_semantics? - @rewindable_io.unlink - @unlinked = true - end - - buffer = "" - while @io.read(1024 * 4, buffer) - entire_buffer_written_out = false - while !entire_buffer_written_out - written = @rewindable_io.write(buffer) - entire_buffer_written_out = written == buffer.size - if !entire_buffer_written_out - buffer.slice!(0 .. written - 1) - end - end - end - @rewindable_io.rewind - end - - def filesystem_has_posix_semantics? - RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/ - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/abstract/id.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/abstract/id.rb deleted file mode 100644 index 218144c..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/abstract/id.rb +++ /dev/null @@ -1,142 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net -# bugrep: Andreas Zehnder - -require 'time' -require 'rack/request' -require 'rack/response' - -module Rack - - module Session - - module Abstract - - # ID sets up a basic framework for implementing an id based sessioning - # service. Cookies sent to the client for maintaining sessions will only - # contain an id reference. Only #get_session and #set_session are - # required to be overwritten. - # - # All parameters are optional. - # * :key determines the name of the cookie, by default it is - # 'rack.session' - # * :path, :domain, :expire_after, :secure, and :httponly set the related - # cookie options as by Rack::Response#add_cookie - # * :defer will not set a cookie in the response. - # * :renew (implementation dependent) will prompt the generation of a new - # session id, and migration of data to be referenced at the new id. If - # :defer is set, it will be overridden and the cookie will be set. - # * :sidbits sets the number of bits in length that a generated session - # id will be. - # - # These options can be set on a per request basis, at the location of - # env['rack.session.options']. Additionally the id of the session can be - # found within the options hash at the key :id. It is highly not - # recommended to change its value. - # - # Is Rack::Utils::Context compatible. - - class ID - DEFAULT_OPTIONS = { - :path => '/', - :domain => nil, - :expire_after => nil, - :secure => false, - :httponly => true, - :defer => false, - :renew => false, - :sidbits => 128 - } - - attr_reader :key, :default_options - def initialize(app, options={}) - @app = app - @key = options[:key] || "rack.session" - @default_options = self.class::DEFAULT_OPTIONS.merge(options) - end - - def call(env) - context(env) - end - - def context(env, app=@app) - load_session(env) - status, headers, body = app.call(env) - commit_session(env, status, headers, body) - end - - private - - # Generate a new session id using Ruby #rand. The size of the - # session id is controlled by the :sidbits option. - # Monkey patch this to use custom methods for session id generation. - - def generate_sid - "%0#{@default_options[:sidbits] / 4}x" % - rand(2**@default_options[:sidbits] - 1) - end - - # Extracts the session id from provided cookies and passes it and the - # environment to #get_session. It then sets the resulting session into - # 'rack.session', and places options and session metadata into - # 'rack.session.options'. - - def load_session(env) - request = Rack::Request.new(env) - session_id = request.cookies[@key] - - begin - session_id, session = get_session(env, session_id) - env['rack.session'] = session - rescue - env['rack.session'] = Hash.new - end - - env['rack.session.options'] = @default_options. - merge(:id => session_id) - end - - # Acquires the session from the environment and the session id from - # the session options and passes them to #set_session. If successful - # and the :defer option is not true, a cookie will be added to the - # response with the session's id. - - def commit_session(env, status, headers, body) - session = env['rack.session'] - options = env['rack.session.options'] - session_id = options[:id] - - if not session_id = set_session(env, session_id, session, options) - env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.") - [status, headers, body] - elsif options[:defer] and not options[:renew] - env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE - [status, headers, body] - else - cookie = Hash.new - cookie[:value] = session_id - cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? - response = Rack::Response.new(body, status, headers) - response.set_cookie(@key, cookie.merge(options)) - response.to_a - end - end - - # All thread safety and session retrival proceedures should occur here. - # Should return [session_id, session]. - # If nil is provided as the session id, generation of a new valid id - # should occur within. - - def get_session(env, sid) - raise '#get_session not implemented.' - end - - # All thread safety and session storage proceedures should occur here. - # Should return true or false dependant on whether or not the session - # was saved or not. - def set_session(env, sid, session, options) - raise '#set_session not implemented.' - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/cookie.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/cookie.rb deleted file mode 100644 index eace9bd..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/cookie.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'openssl' -require 'rack/request' -require 'rack/response' - -module Rack - - module Session - - # Rack::Session::Cookie provides simple cookie based session management. - # The session is a Ruby Hash stored as base64 encoded marshalled data - # set to :key (default: rack.session). - # When the secret key is set, cookie data is checked for data integrity. - # - # Example: - # - # use Rack::Session::Cookie, :key => 'rack.session', - # :domain => 'foo.com', - # :path => '/', - # :expire_after => 2592000, - # :secret => 'change_me' - # - # All parameters are optional. - - class Cookie - - def initialize(app, options={}) - @app = app - @key = options[:key] || "rack.session" - @secret = options[:secret] - @default_options = {:domain => nil, - :path => "/", - :expire_after => nil}.merge(options) - end - - def call(env) - load_session(env) - status, headers, body = @app.call(env) - commit_session(env, status, headers, body) - end - - private - - def load_session(env) - request = Rack::Request.new(env) - session_data = request.cookies[@key] - - if @secret && session_data - session_data, digest = session_data.split("--") - session_data = nil unless digest == generate_hmac(session_data) - end - - begin - session_data = session_data.unpack("m*").first - session_data = Marshal.load(session_data) - env["rack.session"] = session_data - rescue - env["rack.session"] = Hash.new - end - - env["rack.session.options"] = @default_options.dup - end - - def commit_session(env, status, headers, body) - session_data = Marshal.dump(env["rack.session"]) - session_data = [session_data].pack("m*") - - if @secret - session_data = "#{session_data}--#{generate_hmac(session_data)}" - end - - if session_data.size > (4096 - @key.size) - env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.") - [status, headers, body] - else - options = env["rack.session.options"] - cookie = Hash.new - cookie[:value] = session_data - cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? - response = Rack::Response.new(body, status, headers) - response.set_cookie(@key, cookie.merge(options)) - response.to_a - end - end - - def generate_hmac(data) - OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data) - end - - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/memcache.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/memcache.rb deleted file mode 100644 index 4a65cbf..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/memcache.rb +++ /dev/null @@ -1,109 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net - -require 'rack/session/abstract/id' -require 'memcache' - -module Rack - module Session - # Rack::Session::Memcache provides simple cookie based session management. - # Session data is stored in memcached. The corresponding session key is - # maintained in the cookie. - # You may treat Session::Memcache as you would Session::Pool with the - # following caveats. - # - # * Setting :expire_after to 0 would note to the Memcache server to hang - # onto the session data until it would drop it according to it's own - # specifications. However, the cookie sent to the client would expire - # immediately. - # - # Note that memcache does drop data before it may be listed to expire. For - # a full description of behaviour, please see memcache's documentation. - - class Memcache < Abstract::ID - attr_reader :mutex, :pool - DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \ - :namespace => 'rack:session', - :memcache_server => 'localhost:11211' - - def initialize(app, options={}) - super - - @mutex = Mutex.new - @pool = MemCache. - new @default_options[:memcache_server], @default_options - raise 'No memcache servers' unless @pool.servers.any?{|s|s.alive?} - end - - def generate_sid - loop do - sid = super - break sid unless @pool.get(sid, true) - end - end - - def get_session(env, sid) - session = @pool.get(sid) if sid - @mutex.lock if env['rack.multithread'] - unless sid and session - env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? - session = {} - sid = generate_sid - ret = @pool.add sid, session - raise "Session collision on '#{sid.inspect}'" unless /^STORED/ =~ ret - end - session.instance_variable_set('@old', {}.merge(session)) - return [sid, session] - rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted - warn "#{self} is unable to find server." - warn $!.inspect - return [ nil, {} ] - ensure - @mutex.unlock if env['rack.multithread'] - end - - def set_session(env, session_id, new_session, options) - expiry = options[:expire_after] - expiry = expiry.nil? ? 0 : expiry + 1 - - @mutex.lock if env['rack.multithread'] - session = @pool.get(session_id) || {} - if options[:renew] or options[:drop] - @pool.delete session_id - return false if options[:drop] - session_id = generate_sid - @pool.add session_id, 0 # so we don't worry about cache miss on #set - end - old_session = new_session.instance_variable_get('@old') || {} - session = merge_sessions session_id, old_session, new_session, session - @pool.set session_id, session, expiry - return session_id - rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted - warn "#{self} is unable to find server." - warn $!.inspect - return false - ensure - @mutex.unlock if env['rack.multithread'] - end - - private - - def merge_sessions sid, old, new, cur=nil - cur ||= {} - unless Hash === old and Hash === new - warn 'Bad old or new sessions provided.' - return cur - end - - delete = old.keys - new.keys - warn "//@#{sid}: delete #{delete*','}" if $VERBOSE and not delete.empty? - delete.each{|k| cur.delete k } - - update = new.keys.select{|k| new[k] != old[k] } - warn "//@#{sid}: update #{update*','}" if $VERBOSE and not update.empty? - update.each{|k| cur[k] = new[k] } - - cur - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/pool.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/pool.rb deleted file mode 100644 index f6f8740..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/session/pool.rb +++ /dev/null @@ -1,100 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net -# THANKS: -# apeiros, for session id generation, expiry setup, and threadiness -# sergio, threadiness and bugreps - -require 'rack/session/abstract/id' -require 'thread' - -module Rack - module Session - # Rack::Session::Pool provides simple cookie based session management. - # Session data is stored in a hash held by @pool. - # In the context of a multithreaded environment, sessions being - # committed to the pool is done in a merging manner. - # - # The :drop option is available in rack.session.options if you with to - # explicitly remove the session from the session cache. - # - # Example: - # myapp = MyRackApp.new - # sessioned = Rack::Session::Pool.new(myapp, - # :domain => 'foo.com', - # :expire_after => 2592000 - # ) - # Rack::Handler::WEBrick.run sessioned - - class Pool < Abstract::ID - attr_reader :mutex, :pool - DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false - - def initialize(app, options={}) - super - @pool = Hash.new - @mutex = Mutex.new - end - - def generate_sid - loop do - sid = super - break sid unless @pool.key? sid - end - end - - def get_session(env, sid) - session = @pool[sid] if sid - @mutex.lock if env['rack.multithread'] - unless sid and session - env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? - session = {} - sid = generate_sid - @pool.store sid, session - end - session.instance_variable_set('@old', {}.merge(session)) - return [sid, session] - ensure - @mutex.unlock if env['rack.multithread'] - end - - def set_session(env, session_id, new_session, options) - @mutex.lock if env['rack.multithread'] - session = @pool[session_id] - if options[:renew] or options[:drop] - @pool.delete session_id - return false if options[:drop] - session_id = generate_sid - @pool.store session_id, 0 - end - old_session = new_session.instance_variable_get('@old') || {} - session = merge_sessions session_id, old_session, new_session, session - @pool.store session_id, session - return session_id - rescue - warn "#{new_session.inspect} has been lost." - warn $!.inspect - ensure - @mutex.unlock if env['rack.multithread'] - end - - private - - def merge_sessions sid, old, new, cur=nil - cur ||= {} - unless Hash === old and Hash === new - warn 'Bad old or new sessions provided.' - return cur - end - - delete = old.keys - new.keys - warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty? - delete.each{|k| cur.delete k } - - update = new.keys.select{|k| new[k] != old[k] } - warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty? - update.each{|k| cur[k] = new[k] } - - cur - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showexceptions.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showexceptions.rb deleted file mode 100644 index 697bc41..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showexceptions.rb +++ /dev/null @@ -1,349 +0,0 @@ -require 'ostruct' -require 'erb' -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::ShowExceptions catches all exceptions raised from the app it - # wraps. It shows a useful backtrace with the sourcefile and - # clickable context, the whole Rack environment and the request - # data. - # - # Be careful when you use this on public-facing sites as it could - # reveal information helpful to attackers. - - class ShowExceptions - CONTEXT = 7 - - def initialize(app) - @app = app - @template = ERB.new(TEMPLATE) - end - - def call(env) - @app.call(env) - rescue StandardError, LoadError, SyntaxError => e - backtrace = pretty(env, e) - [500, - {"Content-Type" => "text/html", - "Content-Length" => backtrace.join.size.to_s}, - backtrace] - end - - def pretty(env, exception) - req = Rack::Request.new(env) - path = (req.script_name + req.path_info).squeeze("/") - - frames = exception.backtrace.map { |line| - frame = OpenStruct.new - if line =~ /(.*?):(\d+)(:in `(.*)')?/ - frame.filename = $1 - frame.lineno = $2.to_i - frame.function = $4 - - begin - lineno = frame.lineno-1 - lines = ::File.readlines(frame.filename) - frame.pre_context_lineno = [lineno-CONTEXT, 0].max - frame.pre_context = lines[frame.pre_context_lineno...lineno] - frame.context_line = lines[lineno].chomp - frame.post_context_lineno = [lineno+CONTEXT, lines.size].min - frame.post_context = lines[lineno+1..frame.post_context_lineno] - rescue - end - - frame - else - nil - end - }.compact - - env["rack.errors"].puts "#{exception.class}: #{exception.message}" - env["rack.errors"].puts exception.backtrace.map { |l| "\t" + l } - env["rack.errors"].flush - - [@template.result(binding)] - end - - def h(obj) # :nodoc: - case obj - when String - Utils.escape_html(obj) - else - Utils.escape_html(obj.inspect) - end - end - - # :stopdoc: - -# adapted from Django -# Copyright (c) 2005, the Lawrence Journal-World -# Used under the modified BSD license: -# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 -TEMPLATE = <<'HTML' - - - - - - <%=h exception.class %> at <%=h path %> - - - - - -
-

<%=h exception.class %> at <%=h path %>

-

<%=h exception.message %>

- - - - - - -
Ruby<%=h frames.first.filename %>: in <%=h frames.first.function %>, line <%=h frames.first.lineno %>
Web<%=h req.request_method %> <%=h(req.host + path)%>
- -

Jump to:

- -
- -
-

Traceback (innermost first)

-
    -<% frames.each { |frame| %> -
  • - <%=h frame.filename %>: in <%=h frame.function %> - - <% if frame.context_line %> -
    - <% if frame.pre_context %> -
      - <% frame.pre_context.each { |line| %> -
    1. <%=h line %>
    2. - <% } %> -
    - <% end %> - -
      -
    1. <%=h frame.context_line %>...
    - - <% if frame.post_context %> -
      - <% frame.post_context.each { |line| %> -
    1. <%=h line %>
    2. - <% } %> -
    - <% end %> -
    - <% end %> -
  • -<% } %> -
-
- -
-

Request information

- -

GET

- <% unless req.GET.empty? %> - - - - - - - - - <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val.inspect %>
- <% else %> -

No GET data.

- <% end %> - -

POST

- <% unless req.POST.empty? %> - - - - - - - - - <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val.inspect %>
- <% else %> -

No POST data.

- <% end %> - - - - <% unless req.cookies.empty? %> - - - - - - - - - <% req.cookies.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val.inspect %>
- <% else %> -

No cookie data.

- <% end %> - -

Rack ENV

- - - - - - - - - <% env.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val %>
- -
- -
-

- You're seeing this error because you use Rack::ShowExceptions. -

-
- - - -HTML - - # :startdoc: - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showstatus.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showstatus.rb deleted file mode 100644 index 28258c7..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/showstatus.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'erb' -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::ShowStatus catches all empty responses the app it wraps and - # replaces them with a site explaining the error. - # - # Additional details can be put into rack.showstatus.detail - # and will be shown as HTML. If such details exist, the error page - # is always rendered, even if the reply was not empty. - - class ShowStatus - def initialize(app) - @app = app - @template = ERB.new(TEMPLATE) - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - empty = headers['Content-Length'].to_i <= 0 - - # client or server error, or explicit message - if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"] - req = Rack::Request.new(env) - message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s - detail = env["rack.showstatus.detail"] || message - body = @template.result(binding) - size = Rack::Utils.bytesize(body) - [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]] - else - [status, headers, body] - end - end - - def h(obj) # :nodoc: - case obj - when String - Utils.escape_html(obj) - else - Utils.escape_html(obj.inspect) - end - end - - # :stopdoc: - -# adapted from Django -# Copyright (c) 2005, the Lawrence Journal-World -# Used under the modified BSD license: -# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 -TEMPLATE = <<'HTML' - - - - - <%=h message %> at <%=h req.script_name + req.path_info %> - - - - -
-

<%=h message %> (<%= status.to_i %>)

- - - - - - - - - -
Request Method:<%=h req.request_method %>
Request URL:<%=h req.url %>
-
-
-

<%= detail %>

-
- -
-

- You're seeing this error because you use Rack::ShowStatus. -

-
- - -HTML - - # :startdoc: - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/static.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/static.rb deleted file mode 100644 index 168e8f8..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/static.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Rack - - # The Rack::Static middleware intercepts requests for static files - # (javascript files, images, stylesheets, etc) based on the url prefixes - # passed in the options, and serves them using a Rack::File object. This - # allows a Rack stack to serve both static and dynamic content. - # - # Examples: - # use Rack::Static, :urls => ["/media"] - # will serve all requests beginning with /media from the "media" folder - # located in the current directory (ie media/*). - # - # use Rack::Static, :urls => ["/css", "/images"], :root => "public" - # will serve all requests beginning with /css or /images from the folder - # "public" in the current directory (ie public/css/* and public/images/*) - - class Static - - def initialize(app, options={}) - @app = app - @urls = options[:urls] || ["/favicon.ico"] - root = options[:root] || Dir.pwd - @file_server = Rack::File.new(root) - end - - def call(env) - path = env["PATH_INFO"] - can_serve = @urls.any? { |url| path.index(url) == 0 } - - if can_serve - @file_server.call(env) - else - @app.call(env) - end - end - - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/urlmap.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/urlmap.rb deleted file mode 100644 index fcf6616..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/urlmap.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Rack - # Rack::URLMap takes a hash mapping urls or paths to apps, and - # dispatches accordingly. Support for HTTP/1.1 host names exists if - # the URLs start with http:// or https://. - # - # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part - # relevant for dispatch is in the SCRIPT_NAME, and the rest in the - # PATH_INFO. This should be taken care of when you need to - # reconstruct the URL in order to create links. - # - # URLMap dispatches in such a way that the longest paths are tried - # first, since they are most specific. - - class URLMap - def initialize(map = {}) - remap(map) - end - - def remap(map) - @mapping = map.map { |location, app| - if location =~ %r{\Ahttps?://(.*?)(/.*)} - host, location = $1, $2 - else - host = nil - end - - unless location[0] == ?/ - raise ArgumentError, "paths need to start with /" - end - location = location.chomp('/') - - [host, location, app] - }.sort_by { |(h, l, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first - end - - def call(env) - path = env["PATH_INFO"].to_s.squeeze("/") - script_name = env['SCRIPT_NAME'] - hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT') - @mapping.each { |host, location, app| - next unless (hHost == host || sName == host \ - || (host.nil? && (hHost == sName || hHost == sName+':'+sPort))) - next unless location == path[0, location.size] - next unless path[location.size] == nil || path[location.size] == ?/ - - return app.call( - env.merge( - 'SCRIPT_NAME' => (script_name + location), - 'PATH_INFO' => path[location.size..-1])) - } - [404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]] - end - end -end - diff --git a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/utils.rb b/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/utils.rb deleted file mode 100644 index 42e2e69..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.1.pre/rack/utils.rb +++ /dev/null @@ -1,516 +0,0 @@ -# -*- encoding: binary -*- - -require 'set' -require 'tempfile' - -module Rack - # Rack::Utils contains a grab-bag of useful methods for writing web - # applications adopted from all kinds of Ruby libraries. - - module Utils - # Performs URI escaping so that you can construct proper - # query strings faster. Use this rather than the cgi.rb - # version since it's faster. (Stolen from Camping). - def escape(s) - s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) { - '%'+$1.unpack('H2'*$1.size).join('%').upcase - }.tr(' ', '+') - end - module_function :escape - - # Unescapes a URI escaped string. (Stolen from Camping). - def unescape(s) - s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ - [$1.delete('%')].pack('H*') - } - end - module_function :unescape - - # Stolen from Mongrel, with some small modifications: - # Parses a query string by breaking it up at the '&' - # and ';' characters. You can also use this to parse - # cookies by changing the characters used in the second - # parameter (which defaults to '&;'). - def parse_query(qs, d = '&;') - params = {} - - (qs || '').split(/[#{d}] */n).each do |p| - k, v = unescape(p).split('=', 2) - - if cur = params[k] - if cur.class == Array - params[k] << v - else - params[k] = [cur, v] - end - else - params[k] = v - end - end - - return params - end - module_function :parse_query - - def parse_nested_query(qs, d = '&;') - params = {} - - (qs || '').split(/[#{d}] */n).each do |p| - k, v = unescape(p).split('=', 2) - normalize_params(params, k, v) - end - - return params - end - module_function :parse_nested_query - - def normalize_params(params, name, v = nil) - name =~ %r(\A[\[\]]*([^\[\]]+)\]*) - k = $1 || '' - after = $' || '' - - return if k.empty? - - if after == "" - params[k] = v - elsif after == "[]" - params[k] ||= [] - raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - params[k] << v - elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) - child_key = $1 - params[k] ||= [] - raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) - normalize_params(params[k].last, child_key, v) - else - params[k] << normalize_params({}, child_key, v) - end - else - params[k] ||= {} - raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) - params[k] = normalize_params(params[k], after, v) - end - - return params - end - module_function :normalize_params - - def build_query(params) - params.map { |k, v| - if v.class == Array - build_query(v.map { |x| [k, x] }) - else - escape(k) + "=" + escape(v) - end - }.join("&") - end - module_function :build_query - - def build_nested_query(value, prefix = nil) - case value - when Array - value.map { |v| - build_nested_query(v, "#{prefix}[]") - }.join("&") - when Hash - value.map { |k, v| - build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) - }.join("&") - when String - raise ArgumentError, "value must be a Hash" if prefix.nil? - "#{prefix}=#{escape(value)}" - else - prefix - end - end - module_function :build_nested_query - - # Escape ampersands, brackets and quotes to their HTML/XML entities. - def escape_html(string) - string.to_s.gsub("&", "&"). - gsub("<", "<"). - gsub(">", ">"). - gsub("'", "'"). - gsub('"', """) - end - module_function :escape_html - - def select_best_encoding(available_encodings, accept_encoding) - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html - - expanded_accept_encoding = - accept_encoding.map { |m, q| - if m == "*" - (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] } - else - [[m, q]] - end - }.inject([]) { |mem, list| - mem + list - } - - encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m } - - unless encoding_candidates.include?("identity") - encoding_candidates.push("identity") - end - - expanded_accept_encoding.find_all { |m, q| - q == 0.0 - }.each { |m, _| - encoding_candidates.delete(m) - } - - return (encoding_candidates & available_encodings)[0] - end - module_function :select_best_encoding - - # Return the bytesize of String; uses String#length under Ruby 1.8 and - # String#bytesize under 1.9. - if ''.respond_to?(:bytesize) - def bytesize(string) - string.bytesize - end - else - def bytesize(string) - string.size - end - end - module_function :bytesize - - # Context allows the use of a compatible middleware at different points - # in a request handling stack. A compatible middleware must define - # #context which should take the arguments env and app. The first of which - # would be the request environment. The second of which would be the rack - # application that the request would be forwarded to. - class Context - attr_reader :for, :app - - def initialize(app_f, app_r) - raise 'running context does not respond to #context' unless app_f.respond_to? :context - @for, @app = app_f, app_r - end - - def call(env) - @for.context(env, @app) - end - - def recontext(app) - self.class.new(@for, app) - end - - def context(env, app=@app) - recontext(app).call(env) - end - end - - # A case-insensitive Hash that preserves the original case of a - # header when set. - class HeaderHash < Hash - def initialize(hash={}) - @names = {} - hash.each { |k, v| self[k] = v } - end - - def to_hash - inject({}) do |hash, (k,v)| - if v.respond_to? :to_ary - hash[k] = v.to_ary.join("\n") - else - hash[k] = v - end - hash - end - end - - def [](k) - super @names[k.downcase] - end - - def []=(k, v) - delete k - @names[k.downcase] = k - super k, v - end - - def delete(k) - super @names.delete(k.downcase) - end - - def include?(k) - @names.has_key? k.downcase - end - - alias_method :has_key?, :include? - alias_method :member?, :include? - alias_method :key?, :include? - - def merge!(other) - other.each { |k, v| self[k] = v } - self - end - - def merge(other) - hash = dup - hash.merge! other - end - end - - # Every standard HTTP code mapped to the appropriate message. - # Stolen from Mongrel. - HTTP_STATUS_CODES = { - 100 => 'Continue', - 101 => 'Switching Protocols', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Large', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported' - } - - # Responses with HTTP status codes that should not have an entity body - STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304) - - # A multipart form data parser, adapted from IOWA. - # - # Usually, Rack::Request#POST takes care of calling this. - - module Multipart - class UploadedFile - # The filename, *not* including the path, of the "uploaded" file - attr_reader :original_filename - - # The content type of the "uploaded" file - attr_accessor :content_type - - def initialize(path, content_type = "text/plain", binary = false) - raise "#{path} file does not exist" unless ::File.exist?(path) - @content_type = content_type - @original_filename = ::File.basename(path) - @tempfile = Tempfile.new(@original_filename) - @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding) - @tempfile.binmode if binary - FileUtils.copy_file(path, @tempfile.path) - end - - def path - @tempfile.path - end - alias_method :local_path, :path - - def method_missing(method_name, *args, &block) #:nodoc: - @tempfile.__send__(method_name, *args, &block) - end - end - - EOL = "\r\n" - MULTIPART_BOUNDARY = "AaB03x" - - def self.parse_multipart(env) - unless env['CONTENT_TYPE'] =~ - %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n - nil - else - boundary = "--#{$1}" - - params = {} - buf = "" - content_length = env['CONTENT_LENGTH'].to_i - input = env['rack.input'] - input.rewind - - boundary_size = Utils.bytesize(boundary) + EOL.size - bufsize = 16384 - - content_length -= boundary_size - - read_buffer = '' - - status = input.read(boundary_size, read_buffer) - raise EOFError, "bad content body" unless status == boundary + EOL - - rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n - - loop { - head = nil - body = '' - filename = content_type = name = nil - - until head && buf =~ rx - if !head && i = buf.index(EOL+EOL) - head = buf.slice!(0, i+2) # First \r\n - buf.slice!(0, 2) # Second \r\n - - filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1] - content_type = head[/Content-Type: (.*)#{EOL}/ni, 1] - name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1] - - if content_type || filename - body = Tempfile.new("RackMultipart") - body.binmode if body.respond_to?(:binmode) - end - - next - end - - # Save the read body part. - if head && (boundary_size+4 < buf.size) - body << buf.slice!(0, buf.size - (boundary_size+4)) - end - - c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer) - raise EOFError, "bad content body" if c.nil? || c.empty? - buf << c - content_length -= c.size - end - - # Save the rest. - if i = buf.index(rx) - body << buf.slice!(0, i) - buf.slice!(0, boundary_size+2) - - content_length = -1 if $1 == "--" - end - - if filename == "" - # filename is blank which means no file has been selected - data = nil - elsif filename - body.rewind - - # Take the basename of the upload's original filename. - # This handles the full Windows paths given by Internet Explorer - # (and perhaps other broken user agents) without affecting - # those which give the lone filename. - filename =~ /^(?:.*[:\\\/])?(.*)/m - filename = $1 - - data = {:filename => filename, :type => content_type, - :name => name, :tempfile => body, :head => head} - elsif !filename && content_type - body.rewind - - # Generic multipart cases, not coming from a form - data = {:type => content_type, - :name => name, :tempfile => body, :head => head} - else - data = body - end - - Utils.normalize_params(params, name, data) unless data.nil? - - break if buf.empty? || content_length == -1 - } - - input.rewind - - params - end - end - - def self.build_multipart(params, first = true) - if first - unless params.is_a?(Hash) - raise ArgumentError, "value must be a Hash" - end - - multipart = false - query = lambda { |value| - case value - when Array - value.each(&query) - when Hash - value.values.each(&query) - when UploadedFile - multipart = true - end - } - params.values.each(&query) - return nil unless multipart - end - - flattened_params = Hash.new - - params.each do |key, value| - k = first ? key.to_s : "[#{key}]" - - case value - when Array - value.map { |v| - build_multipart(v, false).each { |subkey, subvalue| - flattened_params["#{k}[]#{subkey}"] = subvalue - } - } - when Hash - build_multipart(value, false).each { |subkey, subvalue| - flattened_params[k + subkey] = subvalue - } - else - flattened_params[k] = value - end - end - - if first - flattened_params.map { |name, file| - if file.respond_to?(:original_filename) - ::File.open(file.path, "rb") do |f| - f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding) -<<-EOF ---#{MULTIPART_BOUNDARY}\r -Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r -Content-Type: #{file.content_type}\r -Content-Length: #{::File.stat(file.path).size}\r -\r -#{f.read}\r -EOF - end - else -<<-EOF ---#{MULTIPART_BOUNDARY}\r -Content-Disposition: form-data; name="#{name}"\r -\r -#{file}\r -EOF - end - }.join + "--#{MULTIPART_BOUNDARY}--\r" - else - flattened_params - end - end - end - end -end -- 1.6.4.2 From cb9429a259af819b4b1356e972388137109189de Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 11 Jun 2009 22:34:23 -0700 Subject: [PATCH 116/779] Update memcache-client to 1.7.4 for cheaper timeouts --- activesupport/lib/active_support/vendor.rb | 4 +- .../vendor/memcache-client-1.6.5/memcache.rb | 935 ----------------- .../vendor/memcache-client-1.7.4/memcache.rb | 1107 ++++++++++++++++++++ 3 files changed, 1109 insertions(+), 937 deletions(-) delete mode 100644 activesupport/lib/active_support/vendor/memcache-client-1.6.5/memcache.rb create mode 100644 activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index 28852e6..ebc62f9 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -9,9 +9,9 @@ end require 'builder' begin - gem 'memcache-client', '>= 1.6.5' + gem 'memcache-client', '>= 1.7.4' rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.6.5" + $:.unshift "#{File.dirname(__FILE__)}/vendor/memcache-client-1.7.4" end begin diff --git a/activesupport/lib/active_support/vendor/memcache-client-1.6.5/memcache.rb b/activesupport/lib/active_support/vendor/memcache-client-1.6.5/memcache.rb deleted file mode 100644 index 4d594c2..0000000 --- a/activesupport/lib/active_support/vendor/memcache-client-1.6.5/memcache.rb +++ /dev/null @@ -1,935 +0,0 @@ -$TESTING = defined?($TESTING) && $TESTING - -require 'socket' -require 'thread' -require 'timeout' -require 'zlib' -require 'digest/sha1' - -## -# A Ruby client library for memcached. -# - -class MemCache - - ## - # The version of MemCache you are using. - - VERSION = '1.6.4.99' - - ## - # Default options for the cache object. - - DEFAULT_OPTIONS = { - :namespace => nil, - :readonly => false, - :multithread => true, - :failover => true, - :timeout => 0.5, - :logger => nil, - } - - ## - # Default memcached port. - - DEFAULT_PORT = 11211 - - ## - # Default memcached server weight. - - DEFAULT_WEIGHT = 1 - - ## - # The namespace for this instance - - attr_reader :namespace - - ## - # The multithread setting for this instance - - attr_reader :multithread - - ## - # The servers this client talks to. Play at your own peril. - - attr_reader :servers - - ## - # Socket timeout limit with this client, defaults to 0.5 sec. - # Set to nil to disable timeouts. - - attr_reader :timeout - - ## - # Should the client try to failover to another server if the - # first server is down? Defaults to true. - - attr_reader :failover - - ## - # Log debug/info/warn/error to the given Logger, defaults to nil. - - attr_reader :logger - - ## - # Accepts a list of +servers+ and a list of +opts+. +servers+ may be - # omitted. See +servers=+ for acceptable server list arguments. - # - # Valid options for +opts+ are: - # - # [:namespace] Prepends this value to all keys added or retrieved. - # [:readonly] Raises an exception on cache writes when true. - # [:multithread] Wraps cache access in a Mutex for thread safety. - # [:failover] Should the client try to failover to another server if the - # first server is down? Defaults to true. - # [:timeout] Time to use as the socket read timeout. Defaults to 0.5 sec, - # set to nil to disable timeouts (this is a major performance penalty in Ruby 1.8). - # [:logger] Logger to use for info/debug output, defaults to nil - # Other options are ignored. - - def initialize(*args) - servers = [] - opts = {} - - case args.length - when 0 then # NOP - when 1 then - arg = args.shift - case arg - when Hash then opts = arg - when Array then servers = arg - when String then servers = [arg] - else raise ArgumentError, 'first argument must be Array, Hash or String' - end - when 2 then - servers, opts = args - else - raise ArgumentError, "wrong number of arguments (#{args.length} for 2)" - end - - opts = DEFAULT_OPTIONS.merge opts - @namespace = opts[:namespace] - @readonly = opts[:readonly] - @multithread = opts[:multithread] - @timeout = opts[:timeout] - @failover = opts[:failover] - @logger = opts[:logger] - @mutex = Mutex.new if @multithread - - logger.info { "memcache-client #{VERSION} #{Array(servers).inspect}" } if logger - - Thread.current[:memcache_client] = self.object_id if !@multithread - - self.servers = servers - end - - ## - # Returns a string representation of the cache object. - - def inspect - "" % - [@servers.length, @namespace, @readonly] - end - - ## - # Returns whether there is at least one active server for the object. - - def active? - not @servers.empty? - end - - ## - # Returns whether or not the cache object was created read only. - - def readonly? - @readonly - end - - ## - # Set the servers that the requests will be distributed between. Entries - # can be either strings of the form "hostname:port" or - # "hostname:port:weight" or MemCache::Server objects. - # - def servers=(servers) - # Create the server objects. - @servers = Array(servers).collect do |server| - case server - when String - host, port, weight = server.split ':', 3 - port ||= DEFAULT_PORT - weight ||= DEFAULT_WEIGHT - Server.new self, host, port, weight - else - server - end - end - - logger.debug { "Servers now: #{@servers.inspect}" } if logger - - # There's no point in doing this if there's only one server - @continuum = create_continuum_for(@servers) if @servers.size > 1 - - @servers - end - - ## - # Decrements the value for +key+ by +amount+ and returns the new value. - # +key+ must already exist. If +key+ is not an integer, it is assumed to be - # 0. +key+ can not be decremented below 0. - - def decr(key, amount = 1) - raise MemCacheError, "Update of readonly cache" if @readonly - with_server(key) do |server, cache_key| - cache_decr server, cache_key, amount - end - rescue TypeError => err - handle_error nil, err - end - - ## - # Retrieves +key+ from memcache. If +raw+ is false, the value will be - # unmarshalled. - - def get(key, raw = false) - with_server(key) do |server, cache_key| - value = cache_get server, cache_key - logger.debug { "GET #{key} from #{server.inspect}: #{value ? value.to_s.size : 'nil'}" } if logger - return nil if value.nil? - value = Marshal.load value unless raw - return value - end - rescue TypeError => err - handle_error nil, err - end - - ## - # Retrieves multiple values from memcached in parallel, if possible. - # - # The memcached protocol supports the ability to retrieve multiple - # keys in a single request. Pass in an array of keys to this method - # and it will: - # - # 1. map the key to the appropriate memcached server - # 2. send a single request to each server that has one or more key values - # - # Returns a hash of values. - # - # cache["a"] = 1 - # cache["b"] = 2 - # cache.get_multi "a", "b" # => { "a" => 1, "b" => 2 } - # - # Note that get_multi assumes the values are marshalled. - - def get_multi(*keys) - raise MemCacheError, 'No active servers' unless active? - - keys.flatten! - key_count = keys.length - cache_keys = {} - server_keys = Hash.new { |h,k| h[k] = [] } - - # map keys to servers - keys.each do |key| - server, cache_key = request_setup key - cache_keys[cache_key] = key - server_keys[server] << cache_key - end - - results = {} - - server_keys.each do |server, keys_for_server| - keys_for_server_str = keys_for_server.join ' ' - begin - values = cache_get_multi server, keys_for_server_str - values.each do |key, value| - results[cache_keys[key]] = Marshal.load value - end - rescue IndexError => e - # Ignore this server and try the others - logger.warn { "Unable to retrieve #{keys_for_server.size} elements from #{server.inspect}: #{e.message}"} if logger - end - end - - return results - rescue TypeError => err - handle_error nil, err - end - - ## - # Increments the value for +key+ by +amount+ and returns the new value. - # +key+ must already exist. If +key+ is not an integer, it is assumed to be - # 0. - - def incr(key, amount = 1) - raise MemCacheError, "Update of readonly cache" if @readonly - with_server(key) do |server, cache_key| - cache_incr server, cache_key, amount - end - rescue TypeError => err - handle_error nil, err - end - - ## - # Add +key+ to the cache with value +value+ that expires in +expiry+ - # seconds. If +raw+ is true, +value+ will not be Marshalled. - # - # Warning: Readers should not call this method in the event of a cache miss; - # see MemCache#add. - - ONE_MB = 1024 * 1024 - - def set(key, value, expiry = 0, raw = false) - raise MemCacheError, "Update of readonly cache" if @readonly - with_server(key) do |server, cache_key| - - value = Marshal.dump value unless raw - logger.debug { "SET #{key} to #{server.inspect}: #{value ? value.to_s.size : 'nil'}" } if logger - - data = value.to_s - raise MemCacheError, "Value too large, memcached can only store 1MB of data per key" if data.size > ONE_MB - - command = "set #{cache_key} 0 #{expiry} #{data.size}\r\n#{data}\r\n" - - with_socket_management(server) do |socket| - socket.write command - result = socket.gets - raise_on_error_response! result - - if result.nil? - server.close - raise MemCacheError, "lost connection to #{server.host}:#{server.port}" - end - - result - end - end - end - - ## - # Add +key+ to the cache with value +value+ that expires in +expiry+ - # seconds, but only if +key+ does not already exist in the cache. - # If +raw+ is true, +value+ will not be Marshalled. - # - # Readers should call this method in the event of a cache miss, not - # MemCache#set or MemCache#[]=. - - def add(key, value, expiry = 0, raw = false) - raise MemCacheError, "Update of readonly cache" if @readonly - with_server(key) do |server, cache_key| - value = Marshal.dump value unless raw - logger.debug { "ADD #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger - command = "add #{cache_key} 0 #{expiry} #{value.to_s.size}\r\n#{value}\r\n" - - with_socket_management(server) do |socket| - socket.write command - result = socket.gets - raise_on_error_response! result - result - end - end - end - - ## - # Removes +key+ from the cache in +expiry+ seconds. - - def delete(key, expiry = 0) - raise MemCacheError, "Update of readonly cache" if @readonly - with_server(key) do |server, cache_key| - with_socket_management(server) do |socket| - socket.write "delete #{cache_key} #{expiry}\r\n" - result = socket.gets - raise_on_error_response! result - result - end - end - end - - ## - # Flush the cache from all memcache servers. - - def flush_all - raise MemCacheError, 'No active servers' unless active? - raise MemCacheError, "Update of readonly cache" if @readonly - - begin - @servers.each do |server| - with_socket_management(server) do |socket| - socket.write "flush_all\r\n" - result = socket.gets - raise_on_error_response! result - result - end - end - rescue IndexError => err - handle_error nil, err - end - end - - ## - # Reset the connection to all memcache servers. This should be called if - # there is a problem with a cache lookup that might have left the connection - # in a corrupted state. - - def reset - @servers.each { |server| server.close } - end - - ## - # Returns statistics for each memcached server. An explanation of the - # statistics can be found in the memcached docs: - # - # http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt - # - # Example: - # - # >> pp CACHE.stats - # {"localhost:11211"=> - # {"bytes"=>4718, - # "pid"=>20188, - # "connection_structures"=>4, - # "time"=>1162278121, - # "pointer_size"=>32, - # "limit_maxbytes"=>67108864, - # "cmd_get"=>14532, - # "version"=>"1.2.0", - # "bytes_written"=>432583, - # "cmd_set"=>32, - # "get_misses"=>0, - # "total_connections"=>19, - # "curr_connections"=>3, - # "curr_items"=>4, - # "uptime"=>1557, - # "get_hits"=>14532, - # "total_items"=>32, - # "rusage_system"=>0.313952, - # "rusage_user"=>0.119981, - # "bytes_read"=>190619}} - # => nil - - def stats - raise MemCacheError, "No active servers" unless active? - server_stats = {} - - @servers.each do |server| - next unless server.alive? - - with_socket_management(server) do |socket| - value = nil - socket.write "stats\r\n" - stats = {} - while line = socket.gets do - raise_on_error_response! line - break if line == "END\r\n" - if line =~ /\ASTAT ([\S]+) ([\w\.\:]+)/ then - name, value = $1, $2 - stats[name] = case name - when 'version' - value - when 'rusage_user', 'rusage_system' then - seconds, microseconds = value.split(/:/, 2) - microseconds ||= 0 - Float(seconds) + (Float(microseconds) / 1_000_000) - else - if value =~ /\A\d+\Z/ then - value.to_i - else - value - end - end - end - end - server_stats["#{server.host}:#{server.port}"] = stats - end - end - - raise MemCacheError, "No active servers" if server_stats.empty? - server_stats - end - - ## - # Shortcut to get a value from the cache. - - alias [] get - - ## - # Shortcut to save a value in the cache. This method does not set an - # expiration on the entry. Use set to specify an explicit expiry. - - def []=(key, value) - set key, value - end - - protected unless $TESTING - - ## - # Create a key for the cache, incorporating the namespace qualifier if - # requested. - - def make_cache_key(key) - if namespace.nil? then - key - else - "#{@namespace}:#{key}" - end - end - - ## - # Returns an interoperable hash value for +key+. (I think, docs are - # sketchy for down servers). - - def hash_for(key) - Zlib.crc32(key) - end - - ## - # Pick a server to handle the request based on a hash of the key. - - def get_server_for_key(key, options = {}) - raise ArgumentError, "illegal character in key #{key.inspect}" if - key =~ /\s/ - raise ArgumentError, "key too long #{key.inspect}" if key.length > 250 - raise MemCacheError, "No servers available" if @servers.empty? - return @servers.first if @servers.length == 1 - - hkey = hash_for(key) - - 20.times do |try| - entryidx = Continuum.binary_search(@continuum, hkey) - server = @continuum[entryidx].server - return server if server.alive? - break unless failover - hkey = hash_for "#{try}#{key}" - end - - raise MemCacheError, "No servers available" - end - - ## - # Performs a raw decr for +cache_key+ from +server+. Returns nil if not - # found. - - def cache_decr(server, cache_key, amount) - with_socket_management(server) do |socket| - socket.write "decr #{cache_key} #{amount}\r\n" - text = socket.gets - raise_on_error_response! text - return nil if text == "NOT_FOUND\r\n" - return text.to_i - end - end - - ## - # Fetches the raw data for +cache_key+ from +server+. Returns nil on cache - # miss. - - def cache_get(server, cache_key) - with_socket_management(server) do |socket| - socket.write "get #{cache_key}\r\n" - keyline = socket.gets # "VALUE \r\n" - - if keyline.nil? then - server.close - raise MemCacheError, "lost connection to #{server.host}:#{server.port}" - end - - raise_on_error_response! keyline - return nil if keyline == "END\r\n" - - unless keyline =~ /(\d+)\r/ then - server.close - raise MemCacheError, "unexpected response #{keyline.inspect}" - end - value = socket.read $1.to_i - socket.read 2 # "\r\n" - socket.gets # "END\r\n" - return value - end - end - - ## - # Fetches +cache_keys+ from +server+ using a multi-get. - - def cache_get_multi(server, cache_keys) - with_socket_management(server) do |socket| - values = {} - socket.write "get #{cache_keys}\r\n" - - while keyline = socket.gets do - return values if keyline == "END\r\n" - raise_on_error_response! keyline - - unless keyline =~ /\AVALUE (.+) (.+) (.+)/ then - server.close - raise MemCacheError, "unexpected response #{keyline.inspect}" - end - - key, data_length = $1, $3 - values[$1] = socket.read data_length.to_i - socket.read(2) # "\r\n" - end - - server.close - raise MemCacheError, "lost connection to #{server.host}:#{server.port}" # TODO: retry here too - end - end - - ## - # Performs a raw incr for +cache_key+ from +server+. Returns nil if not - # found. - - def cache_incr(server, cache_key, amount) - with_socket_management(server) do |socket| - socket.write "incr #{cache_key} #{amount}\r\n" - text = socket.gets - raise_on_error_response! text - return nil if text == "NOT_FOUND\r\n" - return text.to_i - end - end - - ## - # Gets or creates a socket connected to the given server, and yields it - # to the block, wrapped in a mutex synchronization if @multithread is true. - # - # If a socket error (SocketError, SystemCallError, IOError) or protocol error - # (MemCacheError) is raised by the block, closes the socket, attempts to - # connect again, and retries the block (once). If an error is again raised, - # reraises it as MemCacheError. - # - # If unable to connect to the server (or if in the reconnect wait period), - # raises MemCacheError. Note that the socket connect code marks a server - # dead for a timeout period, so retrying does not apply to connection attempt - # failures (but does still apply to unexpectedly lost connections etc.). - - def with_socket_management(server, &block) - check_multithread_status! - - @mutex.lock if @multithread - retried = false - - begin - socket = server.socket - - # Raise an IndexError to show this server is out of whack. If were inside - # a with_server block, we'll catch it and attempt to restart the operation. - - raise IndexError, "No connection to server (#{server.status})" if socket.nil? - - block.call(socket) - - rescue SocketError => err - logger.warn { "Socket failure: #{err.message}" } if logger - server.mark_dead(err) - handle_error(server, err) - - rescue MemCacheError, SystemCallError, IOError => err - logger.warn { "Generic failure: #{err.class.name}: #{err.message}" } if logger - handle_error(server, err) if retried || socket.nil? - retried = true - retry - end - ensure - @mutex.unlock if @multithread - end - - def with_server(key) - retried = false - begin - server, cache_key = request_setup(key) - yield server, cache_key - rescue IndexError => e - logger.warn { "Server failed: #{e.class.name}: #{e.message}" } if logger - if !retried && @servers.size > 1 - logger.info { "Connection to server #{server.inspect} DIED! Retrying operation..." } if logger - retried = true - retry - end - handle_error(nil, e) - end - end - - ## - # Handles +error+ from +server+. - - def handle_error(server, error) - raise error if error.is_a?(MemCacheError) - server.close if server - new_error = MemCacheError.new error.message - new_error.set_backtrace error.backtrace - raise new_error - end - - ## - # Performs setup for making a request with +key+ from memcached. Returns - # the server to fetch the key from and the complete key to use. - - def request_setup(key) - raise MemCacheError, 'No active servers' unless active? - cache_key = make_cache_key key - server = get_server_for_key cache_key - return server, cache_key - end - - def raise_on_error_response!(response) - if response =~ /\A(?:CLIENT_|SERVER_)?ERROR(.*)/ - raise MemCacheError, $1.strip - end - end - - def create_continuum_for(servers) - total_weight = servers.inject(0) { |memo, srv| memo + srv.weight } - continuum = [] - - servers.each do |server| - entry_count_for(server, servers.size, total_weight).times do |idx| - hash = Digest::SHA1.hexdigest("#{server.host}:#{server.port}:#{idx}") - value = Integer("0x#{hash[0..7]}") - continuum << Continuum::Entry.new(value, server) - end - end - - continuum.sort { |a, b| a.value <=> b.value } - end - - def entry_count_for(server, total_servers, total_weight) - ((total_servers * Continuum::POINTS_PER_SERVER * server.weight) / Float(total_weight)).floor - end - - def check_multithread_status! - return if @multithread - - if Thread.current[:memcache_client] != self.object_id - raise MemCacheError, <<-EOM - You are accessing this memcache-client instance from multiple threads but have not enabled multithread support. - Normally: MemCache.new(['localhost:11211'], :multithread => true) - In Rails: config.cache_store = [:mem_cache_store, 'localhost:11211', { :multithread => true }] - EOM - end - end - - ## - # This class represents a memcached server instance. - - class Server - - ## - # The amount of time to wait to establish a connection with a memcached - # server. If a connection cannot be established within this time limit, - # the server will be marked as down. - - CONNECT_TIMEOUT = 0.25 - - ## - # The amount of time to wait before attempting to re-establish a - # connection with a server that is marked dead. - - RETRY_DELAY = 30.0 - - ## - # The host the memcached server is running on. - - attr_reader :host - - ## - # The port the memcached server is listening on. - - attr_reader :port - - ## - # The weight given to the server. - - attr_reader :weight - - ## - # The time of next retry if the connection is dead. - - attr_reader :retry - - ## - # A text status string describing the state of the server. - - attr_reader :status - - attr_reader :logger - - ## - # Create a new MemCache::Server object for the memcached instance - # listening on the given host and port, weighted by the given weight. - - def initialize(memcache, host, port = DEFAULT_PORT, weight = DEFAULT_WEIGHT) - raise ArgumentError, "No host specified" if host.nil? or host.empty? - raise ArgumentError, "No port specified" if port.nil? or port.to_i.zero? - - @host = host - @port = port.to_i - @weight = weight.to_i - - @sock = nil - @retry = nil - @status = 'NOT CONNECTED' - @timeout = memcache.timeout - @logger = memcache.logger - end - - ## - # Return a string representation of the server object. - - def inspect - "" % [@host, @port, @weight, @status] - end - - ## - # Check whether the server connection is alive. This will cause the - # socket to attempt to connect if it isn't already connected and or if - # the server was previously marked as down and the retry time has - # been exceeded. - - def alive? - !!socket - end - - ## - # Try to connect to the memcached server targeted by this object. - # Returns the connected socket object on success or nil on failure. - - def socket - return @sock if @sock and not @sock.closed? - - @sock = nil - - # If the host was dead, don't retry for a while. - return if @retry and @retry > Time.now - - # Attempt to connect if not already connected. - begin - @sock = @timeout ? TCPTimeoutSocket.new(@host, @port, @timeout) : TCPSocket.new(@host, @port) - - if Socket.constants.include? 'TCP_NODELAY' then - @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1 - end - @retry = nil - @status = 'CONNECTED' - rescue SocketError, SystemCallError, IOError, Timeout::Error => err - logger.warn { "Unable to open socket: #{err.class.name}, #{err.message}" } if logger - mark_dead err - end - - return @sock - end - - ## - # Close the connection to the memcached server targeted by this - # object. The server is not considered dead. - - def close - @sock.close if @sock && !@sock.closed? - @sock = nil - @retry = nil - @status = "NOT CONNECTED" - end - - ## - # Mark the server as dead and close its socket. - - def mark_dead(error) - @sock.close if @sock && !@sock.closed? - @sock = nil - @retry = Time.now + RETRY_DELAY - - reason = "#{error.class.name}: #{error.message}" - @status = sprintf "%s:%s DEAD (%s), will retry at %s", @host, @port, reason, @retry - @logger.info { @status } if @logger - end - - end - - ## - # Base MemCache exception class. - - class MemCacheError < RuntimeError; end - -end - -# TCPSocket facade class which implements timeouts. -class TCPTimeoutSocket - - def initialize(host, port, timeout) - Timeout::timeout(MemCache::Server::CONNECT_TIMEOUT, SocketError) do - @sock = TCPSocket.new(host, port) - @len = timeout - end - end - - def write(*args) - Timeout::timeout(@len, SocketError) do - @sock.write(*args) - end - end - - def gets(*args) - Timeout::timeout(@len, SocketError) do - @sock.gets(*args) - end - end - - def read(*args) - Timeout::timeout(@len, SocketError) do - @sock.read(*args) - end - end - - def _socket - @sock - end - - def method_missing(meth, *args) - @sock.__send__(meth, *args) - end - - def closed? - @sock.closed? - end - - def close - @sock.close - end -end - -module Continuum - POINTS_PER_SERVER = 160 # this is the default in libmemcached - - # Find the closest index in Continuum with value <= the given value - def self.binary_search(ary, value, &block) - upper = ary.size - 1 - lower = 0 - idx = 0 - - while(lower <= upper) do - idx = (lower + upper) / 2 - comp = ary[idx].value <=> value - - if comp == 0 - return idx - elsif comp > 0 - upper = idx - 1 - else - lower = idx + 1 - end - end - return upper - end - - class Entry - attr_reader :value - attr_reader :server - - def initialize(val, srv) - @value = val - @server = srv - end - - def inspect - "<#{value}, #{server.host}:#{server.port}>" - end - end -end diff --git a/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb b/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb new file mode 100644 index 0000000..f249da7 --- /dev/null +++ b/activesupport/lib/active_support/vendor/memcache-client-1.7.4/memcache.rb @@ -0,0 +1,1107 @@ +$TESTING = defined?($TESTING) && $TESTING + +require 'socket' +require 'thread' +require 'zlib' +require 'digest/sha1' +require 'net/protocol' + +## +# A Ruby client library for memcached. +# + +class MemCache + + ## + # The version of MemCache you are using. + + VERSION = '1.7.4' + + ## + # Default options for the cache object. + + DEFAULT_OPTIONS = { + :namespace => nil, + :readonly => false, + :multithread => true, + :failover => true, + :timeout => 0.5, + :logger => nil, + :no_reply => false, + } + + ## + # Default memcached port. + + DEFAULT_PORT = 11211 + + ## + # Default memcached server weight. + + DEFAULT_WEIGHT = 1 + + ## + # The namespace for this instance + + attr_reader :namespace + + ## + # The multithread setting for this instance + + attr_reader :multithread + + ## + # The servers this client talks to. Play at your own peril. + + attr_reader :servers + + ## + # Socket timeout limit with this client, defaults to 0.5 sec. + # Set to nil to disable timeouts. + + attr_reader :timeout + + ## + # Should the client try to failover to another server if the + # first server is down? Defaults to true. + + attr_reader :failover + + ## + # Log debug/info/warn/error to the given Logger, defaults to nil. + + attr_reader :logger + + ## + # Don't send or look for a reply from the memcached server for write operations. + # Please note this feature only works in memcached 1.2.5 and later. Earlier + # versions will reply with "ERROR". + attr_reader :no_reply + + ## + # Accepts a list of +servers+ and a list of +opts+. +servers+ may be + # omitted. See +servers=+ for acceptable server list arguments. + # + # Valid options for +opts+ are: + # + # [:namespace] Prepends this value to all keys added or retrieved. + # [:readonly] Raises an exception on cache writes when true. + # [:multithread] Wraps cache access in a Mutex for thread safety. Defaults to true. + # [:failover] Should the client try to failover to another server if the + # first server is down? Defaults to true. + # [:timeout] Time to use as the socket read timeout. Defaults to 0.5 sec, + # set to nil to disable timeouts (this is a major performance penalty in Ruby 1.8, + # "gem install SystemTimer' to remove most of the penalty). + # [:logger] Logger to use for info/debug output, defaults to nil + # [:no_reply] Don't bother looking for a reply for write operations (i.e. they + # become 'fire and forget'), memcached 1.2.5 and later only, speeds up + # set/add/delete/incr/decr significantly. + # + # Other options are ignored. + + def initialize(*args) + servers = [] + opts = {} + + case args.length + when 0 then # NOP + when 1 then + arg = args.shift + case arg + when Hash then opts = arg + when Array then servers = arg + when String then servers = [arg] + else raise ArgumentError, 'first argument must be Array, Hash or String' + end + when 2 then + servers, opts = args + else + raise ArgumentError, "wrong number of arguments (#{args.length} for 2)" + end + + opts = DEFAULT_OPTIONS.merge opts + @namespace = opts[:namespace] + @readonly = opts[:readonly] + @multithread = opts[:multithread] + @timeout = opts[:timeout] + @failover = opts[:failover] + @logger = opts[:logger] + @no_reply = opts[:no_reply] + @mutex = Mutex.new if @multithread + + logger.info { "memcache-client #{VERSION} #{Array(servers).inspect}" } if logger + + Thread.current[:memcache_client] = self.object_id if !@multithread + + self.servers = servers + end + + ## + # Returns a string representation of the cache object. + + def inspect + "" % + [@servers.length, @namespace, @readonly] + end + + ## + # Returns whether there is at least one active server for the object. + + def active? + not @servers.empty? + end + + ## + # Returns whether or not the cache object was created read only. + + def readonly? + @readonly + end + + ## + # Set the servers that the requests will be distributed between. Entries + # can be either strings of the form "hostname:port" or + # "hostname:port:weight" or MemCache::Server objects. + # + def servers=(servers) + # Create the server objects. + @servers = Array(servers).collect do |server| + case server + when String + host, port, weight = server.split ':', 3 + port ||= DEFAULT_PORT + weight ||= DEFAULT_WEIGHT + Server.new self, host, port, weight + else + server + end + end + + logger.debug { "Servers now: #{@servers.inspect}" } if logger + + # There's no point in doing this if there's only one server + @continuum = create_continuum_for(@servers) if @servers.size > 1 + + @servers + end + + ## + # Decrements the value for +key+ by +amount+ and returns the new value. + # +key+ must already exist. If +key+ is not an integer, it is assumed to be + # 0. +key+ can not be decremented below 0. + + def decr(key, amount = 1) + raise MemCacheError, "Update of readonly cache" if @readonly + with_server(key) do |server, cache_key| + cache_decr server, cache_key, amount + end + rescue TypeError => err + handle_error nil, err + end + + ## + # Retrieves +key+ from memcache. If +raw+ is false, the value will be + # unmarshalled. + + def get(key, raw = false) + with_server(key) do |server, cache_key| + logger.debug { "get #{key} from #{server.inspect}" } if logger + value = cache_get server, cache_key + return nil if value.nil? + value = Marshal.load value unless raw + return value + end + rescue TypeError => err + handle_error nil, err + end + + ## + # Performs a +get+ with the given +key+. If + # the value does not exist and a block was given, + # the block will be called and the result saved via +add+. + # + # If you do not provide a block, using this + # method is the same as using +get+. + # + def fetch(key, expiry = 0, raw = false) + value = get(key, raw) + + if value.nil? && block_given? + value = yield + add(key, value, expiry, raw) + end + + value + end + + ## + # Retrieves multiple values from memcached in parallel, if possible. + # + # The memcached protocol supports the ability to retrieve multiple + # keys in a single request. Pass in an array of keys to this method + # and it will: + # + # 1. map the key to the appropriate memcached server + # 2. send a single request to each server that has one or more key values + # + # Returns a hash of values. + # + # cache["a"] = 1 + # cache["b"] = 2 + # cache.get_multi "a", "b" # => { "a" => 1, "b" => 2 } + # + # Note that get_multi assumes the values are marshalled. + + def get_multi(*keys) + raise MemCacheError, 'No active servers' unless active? + + keys.flatten! + key_count = keys.length + cache_keys = {} + server_keys = Hash.new { |h,k| h[k] = [] } + + # map keys to servers + keys.each do |key| + server, cache_key = request_setup key + cache_keys[cache_key] = key + server_keys[server] << cache_key + end + + results = {} + + server_keys.each do |server, keys_for_server| + keys_for_server_str = keys_for_server.join ' ' + begin + values = cache_get_multi server, keys_for_server_str + values.each do |key, value| + results[cache_keys[key]] = Marshal.load value + end + rescue IndexError => e + # Ignore this server and try the others + logger.warn { "Unable to retrieve #{keys_for_server.size} elements from #{server.inspect}: #{e.message}"} if logger + end + end + + return results + rescue TypeError => err + handle_error nil, err + end + + ## + # Increments the value for +key+ by +amount+ and returns the new value. + # +key+ must already exist. If +key+ is not an integer, it is assumed to be + # 0. + + def incr(key, amount = 1) + raise MemCacheError, "Update of readonly cache" if @readonly + with_server(key) do |server, cache_key| + cache_incr server, cache_key, amount + end + rescue TypeError => err + handle_error nil, err + end + + ## + # Add +key+ to the cache with value +value+ that expires in +expiry+ + # seconds. If +raw+ is true, +value+ will not be Marshalled. + # + # Warning: Readers should not call this method in the event of a cache miss; + # see MemCache#add. + + ONE_MB = 1024 * 1024 + + def set(key, value, expiry = 0, raw = false) + raise MemCacheError, "Update of readonly cache" if @readonly + with_server(key) do |server, cache_key| + + value = Marshal.dump value unless raw + logger.debug { "set #{key} to #{server.inspect}: #{value.to_s.size}" } if logger + + raise MemCacheError, "Value too large, memcached can only store 1MB of data per key" if value.to_s.size > ONE_MB + + command = "set #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n" + + with_socket_management(server) do |socket| + socket.write command + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + + if result.nil? + server.close + raise MemCacheError, "lost connection to #{server.host}:#{server.port}" + end + + result + end + end + end + + ## + # "cas" is a check and set operation which means "store this data but + # only if no one else has updated since I last fetched it." This can + # be used as a form of optimistic locking. + # + # Works in block form like so: + # cache.cas('some-key') do |value| + # value + 1 + # end + # + # Returns: + # +nil+ if the value was not found on the memcached server. + # +STORED+ if the value was updated successfully + # +EXISTS+ if the value was updated by someone else since last fetch + + def cas(key, expiry=0, raw=false) + raise MemCacheError, "Update of readonly cache" if @readonly + raise MemCacheError, "A block is required" unless block_given? + + (value, token) = gets(key, raw) + return nil unless value + updated = yield value + + with_server(key) do |server, cache_key| + + value = Marshal.dump updated unless raw + logger.debug { "cas #{key} to #{server.inspect}: #{value.to_s.size}" } if logger + command = "cas #{cache_key} 0 #{expiry} #{value.to_s.size} #{token}#{noreply}\r\n#{value}\r\n" + + with_socket_management(server) do |socket| + socket.write command + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + + if result.nil? + server.close + raise MemCacheError, "lost connection to #{server.host}:#{server.port}" + end + + result + end + end + end + + ## + # Add +key+ to the cache with value +value+ that expires in +expiry+ + # seconds, but only if +key+ does not already exist in the cache. + # If +raw+ is true, +value+ will not be Marshalled. + # + # Readers should call this method in the event of a cache miss, not + # MemCache#set. + + def add(key, value, expiry = 0, raw = false) + raise MemCacheError, "Update of readonly cache" if @readonly + with_server(key) do |server, cache_key| + value = Marshal.dump value unless raw + logger.debug { "add #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger + command = "add #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n" + + with_socket_management(server) do |socket| + socket.write command + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + result + end + end + end + + ## + # Add +key+ to the cache with value +value+ that expires in +expiry+ + # seconds, but only if +key+ already exists in the cache. + # If +raw+ is true, +value+ will not be Marshalled. + def replace(key, value, expiry = 0, raw = false) + raise MemCacheError, "Update of readonly cache" if @readonly + with_server(key) do |server, cache_key| + value = Marshal.dump value unless raw + logger.debug { "replace #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger + command = "replace #{cache_key} 0 #{expiry} #{value.to_s.size}#{noreply}\r\n#{value}\r\n" + + with_socket_management(server) do |socket| + socket.write command + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + result + end + end + end + + ## + # Append - 'add this data to an existing key after existing data' + # Please note the value is always passed to memcached as raw since it + # doesn't make a lot of sense to concatenate marshalled data together. + def append(key, value) + raise MemCacheError, "Update of readonly cache" if @readonly + with_server(key) do |server, cache_key| + logger.debug { "append #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger + command = "append #{cache_key} 0 0 #{value.to_s.size}#{noreply}\r\n#{value}\r\n" + + with_socket_management(server) do |socket| + socket.write command + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + result + end + end + end + + ## + # Prepend - 'add this data to an existing key before existing data' + # Please note the value is always passed to memcached as raw since it + # doesn't make a lot of sense to concatenate marshalled data together. + def prepend(key, value) + raise MemCacheError, "Update of readonly cache" if @readonly + with_server(key) do |server, cache_key| + logger.debug { "prepend #{key} to #{server}: #{value ? value.to_s.size : 'nil'}" } if logger + command = "prepend #{cache_key} 0 0 #{value.to_s.size}#{noreply}\r\n#{value}\r\n" + + with_socket_management(server) do |socket| + socket.write command + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + result + end + end + end + + ## + # Removes +key+ from the cache in +expiry+ seconds. + + def delete(key, expiry = 0) + raise MemCacheError, "Update of readonly cache" if @readonly + with_server(key) do |server, cache_key| + with_socket_management(server) do |socket| + logger.debug { "delete #{cache_key} on #{server}" } if logger + socket.write "delete #{cache_key} #{expiry}#{noreply}\r\n" + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + result + end + end + end + + ## + # Flush the cache from all memcache servers. + # A non-zero value for +delay+ will ensure that the flush + # is propogated slowly through your memcached server farm. + # The Nth server will be flushed N*delay seconds from now, + # asynchronously so this method returns quickly. + # This prevents a huge database spike due to a total + # flush all at once. + + def flush_all(delay=0) + raise MemCacheError, 'No active servers' unless active? + raise MemCacheError, "Update of readonly cache" if @readonly + + begin + delay_time = 0 + @servers.each do |server| + with_socket_management(server) do |socket| + logger.debug { "flush_all #{delay_time} on #{server}" } if logger + if delay == 0 # older versions of memcached will fail silently otherwise + socket.write "flush_all#{noreply}\r\n" + else + socket.write "flush_all #{delay_time}#{noreply}\r\n" + end + break nil if @no_reply + result = socket.gets + raise_on_error_response! result + result + end + delay_time += delay + end + rescue IndexError => err + handle_error nil, err + end + end + + ## + # Reset the connection to all memcache servers. This should be called if + # there is a problem with a cache lookup that might have left the connection + # in a corrupted state. + + def reset + @servers.each { |server| server.close } + end + + ## + # Returns statistics for each memcached server. An explanation of the + # statistics can be found in the memcached docs: + # + # http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt + # + # Example: + # + # >> pp CACHE.stats + # {"localhost:11211"=> + # {"bytes"=>4718, + # "pid"=>20188, + # "connection_structures"=>4, + # "time"=>1162278121, + # "pointer_size"=>32, + # "limit_maxbytes"=>67108864, + # "cmd_get"=>14532, + # "version"=>"1.2.0", + # "bytes_written"=>432583, + # "cmd_set"=>32, + # "get_misses"=>0, + # "total_connections"=>19, + # "curr_connections"=>3, + # "curr_items"=>4, + # "uptime"=>1557, + # "get_hits"=>14532, + # "total_items"=>32, + # "rusage_system"=>0.313952, + # "rusage_user"=>0.119981, + # "bytes_read"=>190619}} + # => nil + + def stats + raise MemCacheError, "No active servers" unless active? + server_stats = {} + + @servers.each do |server| + next unless server.alive? + + with_socket_management(server) do |socket| + value = nil + socket.write "stats\r\n" + stats = {} + while line = socket.gets do + raise_on_error_response! line + break if line == "END\r\n" + if line =~ /\ASTAT ([\S]+) ([\w\.\:]+)/ then + name, value = $1, $2 + stats[name] = case name + when 'version' + value + when 'rusage_user', 'rusage_system' then + seconds, microseconds = value.split(/:/, 2) + microseconds ||= 0 + Float(seconds) + (Float(microseconds) / 1_000_000) + else + if value =~ /\A\d+\Z/ then + value.to_i + else + value + end + end + end + end + server_stats["#{server.host}:#{server.port}"] = stats + end + end + + raise MemCacheError, "No active servers" if server_stats.empty? + server_stats + end + + ## + # Shortcut to get a value from the cache. + + alias [] get + + ## + # Shortcut to save a value in the cache. This method does not set an + # expiration on the entry. Use set to specify an explicit expiry. + + def []=(key, value) + set key, value + end + + protected unless $TESTING + + ## + # Create a key for the cache, incorporating the namespace qualifier if + # requested. + + def make_cache_key(key) + if namespace.nil? then + key + else + "#{@namespace}:#{key}" + end + end + + ## + # Returns an interoperable hash value for +key+. (I think, docs are + # sketchy for down servers). + + def hash_for(key) + Zlib.crc32(key) + end + + ## + # Pick a server to handle the request based on a hash of the key. + + def get_server_for_key(key, options = {}) + raise ArgumentError, "illegal character in key #{key.inspect}" if + key =~ /\s/ + raise ArgumentError, "key too long #{key.inspect}" if key.length > 250 + raise MemCacheError, "No servers available" if @servers.empty? + return @servers.first if @servers.length == 1 + + hkey = hash_for(key) + + 20.times do |try| + entryidx = Continuum.binary_search(@continuum, hkey) + server = @continuum[entryidx].server + return server if server.alive? + break unless failover + hkey = hash_for "#{try}#{key}" + end + + raise MemCacheError, "No servers available" + end + + ## + # Performs a raw decr for +cache_key+ from +server+. Returns nil if not + # found. + + def cache_decr(server, cache_key, amount) + with_socket_management(server) do |socket| + socket.write "decr #{cache_key} #{amount}#{noreply}\r\n" + break nil if @no_reply + text = socket.gets + raise_on_error_response! text + return nil if text == "NOT_FOUND\r\n" + return text.to_i + end + end + + ## + # Fetches the raw data for +cache_key+ from +server+. Returns nil on cache + # miss. + + def cache_get(server, cache_key) + with_socket_management(server) do |socket| + socket.write "get #{cache_key}\r\n" + keyline = socket.gets # "VALUE \r\n" + + if keyline.nil? then + server.close + raise MemCacheError, "lost connection to #{server.host}:#{server.port}" + end + + raise_on_error_response! keyline + return nil if keyline == "END\r\n" + + unless keyline =~ /(\d+)\r/ then + server.close + raise MemCacheError, "unexpected response #{keyline.inspect}" + end + value = socket.read $1.to_i + socket.read 2 # "\r\n" + socket.gets # "END\r\n" + return value + end + end + + def gets(key, raw = false) + with_server(key) do |server, cache_key| + logger.debug { "gets #{key} from #{server.inspect}" } if logger + result = with_socket_management(server) do |socket| + socket.write "gets #{cache_key}\r\n" + keyline = socket.gets # "VALUE \r\n" + + if keyline.nil? then + server.close + raise MemCacheError, "lost connection to #{server.host}:#{server.port}" + end + + raise_on_error_response! keyline + return nil if keyline == "END\r\n" + + unless keyline =~ /(\d+) (\w+)\r/ then + server.close + raise MemCacheError, "unexpected response #{keyline.inspect}" + end + value = socket.read $1.to_i + socket.read 2 # "\r\n" + socket.gets # "END\r\n" + [value, $2] + end + result[0] = Marshal.load result[0] unless raw + result + end + rescue TypeError => err + handle_error nil, err + end + + + ## + # Fetches +cache_keys+ from +server+ using a multi-get. + + def cache_get_multi(server, cache_keys) + with_socket_management(server) do |socket| + values = {} + socket.write "get #{cache_keys}\r\n" + + while keyline = socket.gets do + return values if keyline == "END\r\n" + raise_on_error_response! keyline + + unless keyline =~ /\AVALUE (.+) (.+) (.+)/ then + server.close + raise MemCacheError, "unexpected response #{keyline.inspect}" + end + + key, data_length = $1, $3 + values[$1] = socket.read data_length.to_i + socket.read(2) # "\r\n" + end + + server.close + raise MemCacheError, "lost connection to #{server.host}:#{server.port}" # TODO: retry here too + end + end + + ## + # Performs a raw incr for +cache_key+ from +server+. Returns nil if not + # found. + + def cache_incr(server, cache_key, amount) + with_socket_management(server) do |socket| + socket.write "incr #{cache_key} #{amount}#{noreply}\r\n" + break nil if @no_reply + text = socket.gets + raise_on_error_response! text + return nil if text == "NOT_FOUND\r\n" + return text.to_i + end + end + + ## + # Gets or creates a socket connected to the given server, and yields it + # to the block, wrapped in a mutex synchronization if @multithread is true. + # + # If a socket error (SocketError, SystemCallError, IOError) or protocol error + # (MemCacheError) is raised by the block, closes the socket, attempts to + # connect again, and retries the block (once). If an error is again raised, + # reraises it as MemCacheError. + # + # If unable to connect to the server (or if in the reconnect wait period), + # raises MemCacheError. Note that the socket connect code marks a server + # dead for a timeout period, so retrying does not apply to connection attempt + # failures (but does still apply to unexpectedly lost connections etc.). + + def with_socket_management(server, &block) + check_multithread_status! + + @mutex.lock if @multithread + retried = false + + begin + socket = server.socket + + # Raise an IndexError to show this server is out of whack. If were inside + # a with_server block, we'll catch it and attempt to restart the operation. + + raise IndexError, "No connection to server (#{server.status})" if socket.nil? + + block.call(socket) + + rescue SocketError, Errno::EAGAIN, Timeout::Error => err + logger.warn { "Socket failure: #{err.message}" } if logger + server.mark_dead(err) + handle_error(server, err) + + rescue MemCacheError, SystemCallError, IOError => err + logger.warn { "Generic failure: #{err.class.name}: #{err.message}" } if logger + handle_error(server, err) if retried || socket.nil? + retried = true + retry + end + ensure + @mutex.unlock if @multithread + end + + def with_server(key) + retried = false + begin + server, cache_key = request_setup(key) + yield server, cache_key + rescue IndexError => e + logger.warn { "Server failed: #{e.class.name}: #{e.message}" } if logger + if !retried && @servers.size > 1 + logger.info { "Connection to server #{server.inspect} DIED! Retrying operation..." } if logger + retried = true + retry + end + handle_error(nil, e) + end + end + + ## + # Handles +error+ from +server+. + + def handle_error(server, error) + raise error if error.is_a?(MemCacheError) + server.close if server + new_error = MemCacheError.new error.message + new_error.set_backtrace error.backtrace + raise new_error + end + + def noreply + @no_reply ? ' noreply' : '' + end + + ## + # Performs setup for making a request with +key+ from memcached. Returns + # the server to fetch the key from and the complete key to use. + + def request_setup(key) + raise MemCacheError, 'No active servers' unless active? + cache_key = make_cache_key key + server = get_server_for_key cache_key + return server, cache_key + end + + def raise_on_error_response!(response) + if response =~ /\A(?:CLIENT_|SERVER_)?ERROR(.*)/ + raise MemCacheError, $1.strip + end + end + + def create_continuum_for(servers) + total_weight = servers.inject(0) { |memo, srv| memo + srv.weight } + continuum = [] + + servers.each do |server| + entry_count_for(server, servers.size, total_weight).times do |idx| + hash = Digest::SHA1.hexdigest("#{server.host}:#{server.port}:#{idx}") + value = Integer("0x#{hash[0..7]}") + continuum << Continuum::Entry.new(value, server) + end + end + + continuum.sort { |a, b| a.value <=> b.value } + end + + def entry_count_for(server, total_servers, total_weight) + ((total_servers * Continuum::POINTS_PER_SERVER * server.weight) / Float(total_weight)).floor + end + + def check_multithread_status! + return if @multithread + + if Thread.current[:memcache_client] != self.object_id + raise MemCacheError, <<-EOM + You are accessing this memcache-client instance from multiple threads but have not enabled multithread support. + Normally: MemCache.new(['localhost:11211'], :multithread => true) + In Rails: config.cache_store = [:mem_cache_store, 'localhost:11211', { :multithread => true }] + EOM + end + end + + ## + # This class represents a memcached server instance. + + class Server + + ## + # The amount of time to wait before attempting to re-establish a + # connection with a server that is marked dead. + + RETRY_DELAY = 30.0 + + ## + # The host the memcached server is running on. + + attr_reader :host + + ## + # The port the memcached server is listening on. + + attr_reader :port + + ## + # The weight given to the server. + + attr_reader :weight + + ## + # The time of next retry if the connection is dead. + + attr_reader :retry + + ## + # A text status string describing the state of the server. + + attr_reader :status + + attr_reader :logger + + ## + # Create a new MemCache::Server object for the memcached instance + # listening on the given host and port, weighted by the given weight. + + def initialize(memcache, host, port = DEFAULT_PORT, weight = DEFAULT_WEIGHT) + raise ArgumentError, "No host specified" if host.nil? or host.empty? + raise ArgumentError, "No port specified" if port.nil? or port.to_i.zero? + + @host = host + @port = port.to_i + @weight = weight.to_i + + @sock = nil + @retry = nil + @status = 'NOT CONNECTED' + @timeout = memcache.timeout + @logger = memcache.logger + end + + ## + # Return a string representation of the server object. + + def inspect + "" % [@host, @port, @weight, @status] + end + + ## + # Check whether the server connection is alive. This will cause the + # socket to attempt to connect if it isn't already connected and or if + # the server was previously marked as down and the retry time has + # been exceeded. + + def alive? + !!socket + end + + ## + # Try to connect to the memcached server targeted by this object. + # Returns the connected socket object on success or nil on failure. + + def socket + return @sock if @sock and not @sock.closed? + + @sock = nil + + # If the host was dead, don't retry for a while. + return if @retry and @retry > Time.now + + # Attempt to connect if not already connected. + begin + @sock = connect_to(@host, @port, @timeout) + @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1 + @retry = nil + @status = 'CONNECTED' + rescue SocketError, SystemCallError, IOError => err + logger.warn { "Unable to open socket: #{err.class.name}, #{err.message}" } if logger + mark_dead err + end + + return @sock + end + + def connect_to(host, port, timeout=nil) + io = MemCache::BufferedIO.new(TCPSocket.new(host, port)) + io.read_timeout = timeout + io + end + + ## + # Close the connection to the memcached server targeted by this + # object. The server is not considered dead. + + def close + @sock.close if @sock && !@sock.closed? + @sock = nil + @retry = nil + @status = "NOT CONNECTED" + end + + ## + # Mark the server as dead and close its socket. + + def mark_dead(error) + @sock.close if @sock && !@sock.closed? + @sock = nil + @retry = Time.now + RETRY_DELAY + + reason = "#{error.class.name}: #{error.message}" + @status = sprintf "%s:%s DEAD (%s), will retry at %s", @host, @port, reason, @retry + @logger.info { @status } if @logger + end + + end + + ## + # Base MemCache exception class. + + class MemCacheError < RuntimeError; end + + class BufferedIO < Net::BufferedIO # :nodoc: + BUFSIZE = 1024 * 16 + + # An implementation similar to this is in *trunk* for 1.9. When it + # gets released, this method can be removed when using 1.9 + def rbuf_fill + begin + @rbuf << @io.read_nonblock(BUFSIZE) + rescue Errno::EWOULDBLOCK + retry unless @read_timeout + if IO.select([@io], nil, nil, @read_timeout) + retry + else + raise Timeout::Error, 'IO timeout' + end + end + end + + def setsockopt *args + @io.setsockopt *args + end + + def gets + readuntil("\n") + end + end + +end + +module Continuum + POINTS_PER_SERVER = 160 # this is the default in libmemcached + + # Find the closest index in Continuum with value <= the given value + def self.binary_search(ary, value, &block) + upper = ary.size - 1 + lower = 0 + idx = 0 + + while(lower <= upper) do + idx = (lower + upper) / 2 + comp = ary[idx].value <=> value + + if comp == 0 + return idx + elsif comp > 0 + upper = idx - 1 + else + lower = idx + 1 + end + end + return upper + end + + class Entry + attr_reader :value + attr_reader :server + + def initialize(val, srv) + @value = val + @server = srv + end + + def inspect + "<#{value}, #{server.host}:#{server.port}>" + end + end + +end -- 1.6.4.2 From 447d60e9ed015e5d6bd336323881f8180c2070d0 Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Sat, 13 Jun 2009 12:43:07 +0200 Subject: [PATCH 117/779] Bytes calculation speed up [#2800 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/active_support/core_ext/numeric/bytes.rb | 24 ++++++++++++------- 1 files changed, 15 insertions(+), 9 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/numeric/bytes.rb b/activesupport/lib/active_support/core_ext/numeric/bytes.rb index 5647767..d3ff615 100644 --- a/activesupport/lib/active_support/core_ext/numeric/bytes.rb +++ b/activesupport/lib/active_support/core_ext/numeric/bytes.rb @@ -3,41 +3,47 @@ module ActiveSupport #:nodoc: module Numeric #:nodoc: # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes module Bytes + KILOBYTE = 1024 + MEGABYTE = KILOBYTE * 1024 + GIGABYTE = MEGABYTE * 1024 + TERABYTE = GIGABYTE * 1024 + PETABYTE = TERABYTE * 1024 + EXABYTE = PETABYTE * 1024 + def bytes self end alias :byte :bytes def kilobytes - self * 1024 + self * KILOBYTE end alias :kilobyte :kilobytes def megabytes - self * 1024.kilobytes + self * MEGABYTE end alias :megabyte :megabytes def gigabytes - self * 1024.megabytes + self * GIGABYTE end alias :gigabyte :gigabytes def terabytes - self * 1024.gigabytes + self * TERABYTE end alias :terabyte :terabytes - + def petabytes - self * 1024.terabytes + self * PETABYTE end alias :petabyte :petabytes - + def exabytes - self * 1024.petabytes + self * EXABYTE end alias :exabyte :exabytes - end end end -- 1.6.4.2 From a491b19502781266b05918cf99b6ba67898e3be9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 15 Jun 2009 10:23:23 -0500 Subject: [PATCH 118/779] Add :concat option to asset tag helpers to force concatenation. This is useful for working around IE's stylesheet limit. stylesheet_link_tag :all, :concat => true --- .../lib/action_view/helpers/asset_tag_helper.rb | 24 ++++++++++++---- actionpack/test/template/asset_tag_helper_test.rb | 30 ++++++++++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index dffb708..babb9db 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -272,14 +272,17 @@ module ActionView # javascript_include_tag :all, :cache => true, :recursive => true def javascript_include_tag(*sources) options = sources.extract_options!.stringify_keys - cache = options.delete("cache") + concat = options.delete("concat") + cache = concat || options.delete("cache") recursive = options.delete("recursive") - if ActionController::Base.perform_caching && cache + if concat || (ActionController::Base.perform_caching && cache) joined_javascript_name = (cache == true ? "all" : cache) + ".js" joined_javascript_path = File.join(joined_javascript_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : JAVASCRIPTS_DIR, joined_javascript_name) - write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path) + unless ActionController::Base.perform_caching && File.exists?(joined_javascript_path) + write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) + end javascript_src_tag(joined_javascript_name, options) else expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n") @@ -410,16 +413,25 @@ module ActionView # The :recursive option is also available for caching: # # stylesheet_link_tag :all, :cache => true, :recursive => true + # + # To force concatenation (even in development mode) set :concat to true. This is useful if + # you have too many stylesheets for IE to load. + # + # stylesheet_link_tag :all, :concat => true + # def stylesheet_link_tag(*sources) options = sources.extract_options!.stringify_keys - cache = options.delete("cache") + concat = options.delete("concat") + cache = concat || options.delete("cache") recursive = options.delete("recursive") - if ActionController::Base.perform_caching && cache + if concat || (ActionController::Base.perform_caching && cache) joined_stylesheet_name = (cache == true ? "all" : cache) + ".css" joined_stylesheet_path = File.join(joined_stylesheet_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : STYLESHEETS_DIR, joined_stylesheet_name) - write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path) + unless ActionController::Base.perform_caching && File.exists?(joined_stylesheet_path) + write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) + end stylesheet_tag(joined_stylesheet_name, options) else expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n") diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 7845746..7ffabff 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -567,6 +567,36 @@ class AssetTagHelperTest < ActionView::TestCase FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute')) end + def test_concat_stylesheet_link_tag_when_caching_off + ENV["RAILS_ASSET_ID"] = "" + + assert_dom_equal( + %(), + stylesheet_link_tag(:all, :concat => true) + ) + + expected = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/*.css"].map { |p| File.mtime(p) }.max + assert_equal expected, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + + assert_dom_equal( + %(), + stylesheet_link_tag(:all, :concat => "money") + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + + assert_dom_equal( + %(), + stylesheet_link_tag(:all, :concat => "/absolute/test") + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.css')) + ensure + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute')) + end + def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host ENV["RAILS_ASSET_ID"] = "" ActionController::Base.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" } -- 1.6.4.2 From b75bc05bc540f2e9efaf41d3a1540787aaa58d19 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 18 Jun 2009 18:06:42 -0400 Subject: [PATCH 119/779] Delegated methods report the expected file/line in backtraces --- .../active_support/core_ext/module/delegation.rb | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index fb4b5f0..cea3fb7 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -110,8 +110,11 @@ class Module allow_nil = options[:allow_nil] && "#{to} && " + file, line = caller.first.split(':', 2) + line = line.to_i + methods.each do |method| - module_eval(<<-EOS, "(__DELEGATION__)", 1) + module_eval(<<-EOS, file, line) def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block) #{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block) end # end -- 1.6.4.2 From 8b9b954f40ef6fdd2ca8c3eb50e35eda9eb115a6 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 18 Jun 2009 20:00:24 -0400 Subject: [PATCH 120/779] Friendlier runtime exception if delegatee is nil --- .../active_support/core_ext/module/delegation.rb | 12 +++++++++--- activesupport/test/core_ext/module_test.rb | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index cea3fb7..9377bff 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -108,15 +108,21 @@ class Module prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_" - allow_nil = options[:allow_nil] && "#{to} && " - file, line = caller.first.split(':', 2) line = line.to_i methods.each do |method| + on_nil = + if options[:allow_nil] + 'return' + else + %(raise "#{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") + end + module_eval(<<-EOS, file, line) def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block) - #{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block) + #{on_nil} if #{to}.nil? + #{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block) end # end EOS end diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb index 0d3d10f..7fb6e14 100644 --- a/activesupport/test/core_ext/module_test.rb +++ b/activesupport/test/core_ext/module_test.rb @@ -141,7 +141,7 @@ class ModuleTest < Test::Unit::TestCase def test_delegation_without_allow_nil_and_nil_value david = Someone.new("David") - assert_raise(NoMethodError) { david.street } + assert_raise(RuntimeError) { david.street } end def test_parent -- 1.6.4.2 From 22554745b7239eca19ea634feca4fcda517b3f97 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 18 Jun 2009 21:31:10 -0400 Subject: [PATCH 121/779] Turn on autolist for debugging also --- railties/lib/test_help.rb | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/railties/lib/test_help.rb b/railties/lib/test_help.rb index ee24ea3..f860892 100644 --- a/railties/lib/test_help.rb +++ b/railties/lib/test_help.rb @@ -29,7 +29,10 @@ end begin require_library_or_gem 'ruby-debug' Debugger.start - Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings) + if Debugger.respond_to?(:settings) + Debugger.settings[:autoeval] = true + Debugger.settings[:autolist] = 1 + end rescue LoadError # ruby-debug wasn't available so neither can the debugging be end -- 1.6.4.2 From 6720b25b2da5e1b81494d4dcc6248f64479a5db8 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 25 Jun 2009 14:44:09 -0500 Subject: [PATCH 122/779] send_data should set Content-Length as a string --- actionpack/lib/action_controller/streaming.rb | 2 +- actionpack/test/controller/send_file_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/streaming.rb b/actionpack/lib/action_controller/streaming.rb index 9f80f48..8a9fbfc 100644 --- a/actionpack/lib/action_controller/streaming.rb +++ b/actionpack/lib/action_controller/streaming.rb @@ -161,7 +161,7 @@ module ActionController #:nodoc: content_type = content_type.to_s.strip # fixes a problem with extra '\r' with some browsers headers.merge!( - 'Content-Length' => options[:length], + 'Content-Length' => options[:length].to_s, 'Content-Type' => content_type, 'Content-Disposition' => disposition, 'Content-Transfer-Encoding' => 'binary' diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index a27e951..26c7f9b 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -108,7 +108,7 @@ class SendFileTest < ActionController::TestCase @controller.send(:send_file_headers!, options) h = @controller.headers - assert_equal 1, h['Content-Length'] + assert_equal '1', h['Content-Length'] assert_equal 'image/png', h['Content-Type'] assert_equal 'disposition; filename="filename"', h['Content-Disposition'] assert_equal 'binary', h['Content-Transfer-Encoding'] -- 1.6.4.2 From 9407f6e9a428b37517fdd779eac15e4f77bb4b71 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sat, 27 Jun 2009 13:11:01 +1200 Subject: [PATCH 123/779] Make filter_parameters work correctly with array parameters. Conflicts: actionpack/lib/action_controller/base/filter_parameter_logging.rb --- actionpack/lib/action_controller/base.rb | 4 ++++ actionpack/test/controller/filter_params_test.rb | 3 ++- 2 files changed, 6 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 95cf3e7..3c89fc8 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -491,6 +491,10 @@ module ActionController #:nodoc: filtered_parameters[key] = '[FILTERED]' elsif value.is_a?(Hash) filtered_parameters[key] = filter_parameters(value) + elsif value.is_a?(Array) + filtered_parameters[key] = value.collect do |item| + filter_parameters(item) + end elsif block_given? key = key.dup value = value.dup if value diff --git a/actionpack/test/controller/filter_params_test.rb b/actionpack/test/controller/filter_params_test.rb index 0b259a7..0f48088 100644 --- a/actionpack/test/controller/filter_params_test.rb +++ b/actionpack/test/controller/filter_params_test.rb @@ -23,7 +23,8 @@ class FilterParamTest < Test::Unit::TestCase [{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'], [{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'], [{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'], - [{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana']] + [{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana'], + [{'baz'=>[{'foo'=>'baz'}]}, {'baz'=>[{'foo'=>'[FILTERED]'}]}, %w(foo)]] test_hashes.each do |before_filter, after_filter, filter_words| FilterParamController.filter_parameter_logging(*filter_words) -- 1.6.4.2 From eb52dc3db7ebab5327869ab33804259912ef10be Mon Sep 17 00:00:00 2001 From: Chris Mear Date: Thu, 19 Feb 2009 14:16:10 +0000 Subject: [PATCH 124/779] Make text_area_tag escape contents by default. Signed-off-by: Michael Koziarski --- .../lib/action_view/helpers/form_tag_helper.rb | 5 +++++ actionpack/test/template/form_tag_helper_test.rb | 12 ++++++++++++ 2 files changed, 17 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index f1cc027..c389307 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -230,6 +230,8 @@ module ActionView # * :rows - Specify the number of rows in the textarea # * :cols - Specify the number of columns in the textarea # * :disabled - If set to true, the user will not be able to use this input. + # * :escape - By default, the contents of the text input are HTML escaped. + # If you need unescaped contents, set this to false. # * Any other key creates standard HTML attributes for the tag. # # ==== Examples @@ -257,6 +259,9 @@ module ActionView options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split) end + escape = options.key?("escape") ? options.delete("escape") : true + content = html_escape(content) if escape + content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index e3a140a..2d988cd 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -158,6 +158,18 @@ class FormTagHelperTest < ActionView::TestCase assert_match VALID_HTML_ID, input_elem['id'] end + def test_text_area_tag_escape_content + actual = text_area_tag "body", "hello world", :size => "20x40" + expected = %() + assert_dom_equal expected, actual + end + + def test_text_area_tag_unescaped_content + actual = text_area_tag "body", "hello world", :size => "20x40", :escape => false + expected = %() + assert_dom_equal expected, actual + end + def test_text_field_tag actual = text_field_tag "title", "Hello!" expected = %() -- 1.6.4.2 From e10305f0f4f3dc6e9f9377ac5cf68e70b6af8474 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 1 Jul 2009 11:16:18 -0700 Subject: [PATCH 125/779] Accept Symbol for contoller name [#2855 state:resolved] Signed-off-by: Yehuda Katz + Carl Lerche --- .../lib/action_controller/routing/route_set.rb | 2 +- actionpack/test/controller/routing_test.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 044ace7..397f06b 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -436,7 +436,7 @@ module ActionController def recognize(request) params = recognize_path(request.path, extract_request_environment(request)) request.path_parameters = params.with_indifferent_access - "#{params[:controller].camelize}Controller".constantize + "#{params[:controller].to_s.camelize}Controller".constantize end def recognize_path(path, environment={}) diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index ef56119..c07f508 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1662,6 +1662,17 @@ class RouteSetTest < Test::Unit::TestCase assert_equal 1, set.routes.size end + def test_draw_symbol_controller_name + assert_equal 0, set.routes.size + set.draw do |map| + map.connect '/users/index', :controller => :users, :action => :index + end + @request = ActionController::TestRequest.new + @request.request_uri = '/users/index' + assert_nothing_raised { set.recognize(@request) } + assert_equal 1, set.routes.size + end + def test_named_draw assert_equal 0, set.routes.size set.draw do |map| -- 1.6.4.2 From a8bd3c8a109fe88a4ce567e4272fd796e42b42da Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 1 Jul 2009 11:53:17 -0700 Subject: [PATCH 126/779] Move mocha down below initial T::U require and bump version to 0.9.7 [#2858 state:resolved] --- actionmailer/test/abstract_unit.rb | 3 --- activerecord/test/cases/helper.rb | 3 +-- activeresource/test/abstract_unit.rb | 4 +--- .../core_ext/module/attribute_accessors.rb | 2 ++ activesupport/lib/active_support/test_case.rb | 14 +++++++------- .../lib/active_support/testing/deprecation.rb | 2 ++ .../vendor/i18n-0.1.3/test/i18n_exceptions_test.rb | 1 - .../vendor/i18n-0.1.3/test/i18n_test.rb | 1 - .../vendor/i18n-0.1.3/test/simple_backend_test.rb | 1 - activesupport/test/abstract_unit.rb | 3 +-- railties/test/abstract_unit.rb | 3 --- 11 files changed, 14 insertions(+), 23 deletions(-) diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 3e77255..9728ae5 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -1,9 +1,6 @@ require 'rubygems' require 'test/unit' -gem 'mocha', '>= 0.9.5' -require 'mocha' - $:.unshift "#{File.dirname(__FILE__)}/../lib" $:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib" $:.unshift "#{File.dirname(__FILE__)}/../../actionpack/lib" diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index 1ec52ac..1ef38c9 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -5,8 +5,7 @@ require 'config' require 'rubygems' require 'test/unit' -gem 'mocha', '>= 0.9.5' -require 'mocha' +require 'stringio' require 'active_record' require 'active_record/test_case' diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb index 0f11ea4..07d201a 100644 --- a/activeresource/test/abstract_unit.rb +++ b/activeresource/test/abstract_unit.rb @@ -1,8 +1,6 @@ require 'rubygems' require 'test/unit' - -gem 'mocha', '>= 0.9.5' -require 'mocha' +require 'active_support/test_case' $:.unshift "#{File.dirname(__FILE__)}/../lib" $:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib" diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb index 9402cb8..9359b22 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -1,3 +1,5 @@ +require "active_support/core_ext/array" + # Extends the module object with module and instance accessors for class attributes, # just like the native attr* accessors for instance attributes. # diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index f05d409..62fe7f5 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -1,5 +1,11 @@ +require 'test/unit/testcase' +require 'active_support/testing/setup_and_teardown' +require 'active_support/testing/assertions' +require 'active_support/testing/deprecation' +require 'active_support/testing/declarative' + begin - gem 'mocha', '>= 0.9.3' + gem 'mocha', ">= 0.9.7" require 'mocha' rescue LoadError # Fake Mocha::ExpectationError so we can rescue it in #run. Bleh. @@ -7,12 +13,6 @@ rescue LoadError Mocha.const_set :ExpectationError, Class.new(StandardError) end -require 'test/unit/testcase' -require 'active_support/testing/setup_and_teardown' -require 'active_support/testing/assertions' -require 'active_support/testing/deprecation' -require 'active_support/testing/declarative' - module ActiveSupport class TestCase < ::Test::Unit::TestCase if defined? MiniTest diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb index e922060..2271caf 100644 --- a/activesupport/lib/active_support/testing/deprecation.rb +++ b/activesupport/lib/active_support/testing/deprecation.rb @@ -1,3 +1,5 @@ +require "active_support/core_ext/module" + module ActiveSupport module Testing module Deprecation #:nodoc: diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb index dfcba69..4e78e71 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_exceptions_test.rb @@ -2,7 +2,6 @@ $:.unshift "lib" require 'rubygems' require 'test/unit' -require 'mocha' require 'i18n' require 'active_support' diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb index 50d6832..2835ec4 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/i18n_test.rb @@ -2,7 +2,6 @@ $:.unshift "lib" require 'rubygems' require 'test/unit' -require 'mocha' require 'i18n' require 'active_support' diff --git a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb index 65f3ac1..a1696c7 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.1.3/test/simple_backend_test.rb @@ -3,7 +3,6 @@ $:.unshift "lib" require 'rubygems' require 'test/unit' -require 'mocha' require 'i18n' require 'time' require 'yaml' diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index a0c0c59..9ded5f8 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -1,8 +1,7 @@ require 'rubygems' require 'test/unit' -gem 'mocha', '>= 0.9.5' -require 'mocha' +ENV['NO_RELOAD'] = '1' $:.unshift "#{File.dirname(__FILE__)}/../lib" require 'active_support' diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb index 0addcb8..85f680d 100644 --- a/railties/test/abstract_unit.rb +++ b/railties/test/abstract_unit.rb @@ -9,9 +9,6 @@ require 'stringio' require 'rubygems' require 'test/unit' -gem 'mocha', '>= 0.9.5' -require 'mocha' - require 'active_support' require 'active_support/test_case' -- 1.6.4.2 From 97ad9361487e7600ba870c8cd7d3b922ed7395d2 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 1 Jul 2009 13:48:27 -0700 Subject: [PATCH 127/779] Updates CI to use latest mocha --- ci/geminstaller.yml | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml index 387e370..a04bf94 100644 --- a/ci/geminstaller.yml +++ b/ci/geminstaller.yml @@ -7,7 +7,7 @@ gems: - name: memcache-client version: >= 1.5.0 - name: mocha - version: >= 0.9.5 + version: >= 0.9.7 - name: mysql #version: >= 2.7 version: = 2.7 -- 1.6.4.2 From 5217c16b094a07919348b52aa164ca2fd26b5d5e Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 1 Jul 2009 16:22:17 -0700 Subject: [PATCH 128/779] JSON.escape returns UTF-8 strings [#2849 state:resolved] --- activesupport/lib/active_support/json/encoding.rb | 12 +++++++----- activesupport/test/json/encoding_test.rb | 9 +++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 63d1e71..ffd2050 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,4 +1,4 @@ -# encoding: binary +# encoding: utf-8 require 'active_support/core_ext/module/delegation' require 'active_support/deprecation' @@ -51,12 +51,14 @@ module ActiveSupport def escape(string) string = string.dup.force_encoding(::Encoding::BINARY) if string.respond_to?(:force_encoding) - json = '"' + string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] } - json.gsub(/([\xC0-\xDF][\x80-\xBF]| + json = string. + gsub(escape_regex) { |s| ESCAPED_CHARS[s] }. + gsub(/([\xC0-\xDF][\x80-\xBF]| [\xE0-\xEF][\x80-\xBF]{2}| [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| - s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') - } + '"' + s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&') + } + %("#{json}") end # Converts a Ruby object into a JSON string. diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index b42e329..ab8e4e8 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -74,8 +74,13 @@ class TestJSONEncoding < Test::Unit::TestCase def test_utf8_string_encoded_properly_when_kcode_is_utf8 with_kcode 'UTF8' do - assert_equal '"\\u20ac2.99"', ActiveSupport::JSON.encode('€2.99') - assert_equal '"\\u270e\\u263a"', ActiveSupport::JSON.encode('✎☺') + result = ActiveSupport::JSON.encode('€2.99') + assert_equal '"\\u20ac2.99"', result + assert_equal(Encoding::UTF_8, result.encoding) if result.respond_to?(:encoding) + + result = ActiveSupport::JSON.encode('✎☺') + assert_equal '"\\u270e\\u263a"', result + assert_equal(Encoding::UTF_8, result.encoding) if result.respond_to?(:encoding) end end -- 1.6.4.2 From 4d8fd8d33568d8db9fc7a2c0bc2e11ca67c77987 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 1 Jul 2009 16:56:43 -0700 Subject: [PATCH 129/779] Fixes bug where Memcached connections get corrupted when an invalid expire is passed in [#2854 state:resolved] --- activesupport/lib/active_support/cache.rb | 15 ++++++++++++++- .../lib/active_support/cache/mem_cache_store.rb | 4 ---- activesupport/test/caching_test.rb | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 83174d3..3f31185 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -91,11 +91,16 @@ module ActiveSupport class Store cattr_accessor :logger + attr_reader :silence, :logger_off + def silence! @silence = true self end + alias silence? silence + alias logger_off? logger_off + # Fetches data from the cache, using the given key. If there is data in # the cache with the given key, then that data is returned. # @@ -220,8 +225,16 @@ module ActiveSupport end private + def expires_in(options) + expires_in = options && options[:expires_in] + + raise ":expires_in must be a number" if expires_in && !expires_in.is_a?(Numeric) + + expires_in || 0 + end + def log(operation, key, options) - logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !@silence && !@logger_off + logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !silence? && !logger_off? end end end diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 38b3409..954d0f5 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -130,10 +130,6 @@ module ActiveSupport end private - def expires_in(options) - (options && options[:expires_in]) || 0 - end - def raw?(options) options && options[:raw] end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 81d0acf..94c130d 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -1,3 +1,4 @@ +require 'logger' require 'abstract_unit' class CacheKeyTest < ActiveSupport::TestCase @@ -173,6 +174,8 @@ uses_memcached 'memcached backed store' do @cache = ActiveSupport::Cache.lookup_store(:mem_cache_store) @data = @cache.instance_variable_get(:@data) @cache.clear + @cache.silence! + @cache.logger = Logger.new("/dev/null") end include CacheStoreBehavior @@ -295,6 +298,22 @@ uses_memcached 'memcached backed store' do app = @cache.middleware.new(app) app.call({}) end + + def test_expires_in + result = @cache.write('foo', 'bar', :expires_in => 1) + assert_equal 'bar', @cache.read('foo') + sleep 2 + assert_equal nil, @cache.read('foo') + end + + def test_expires_in_with_invalid_value + @cache.write('baz', 'bat') + assert_raise(RuntimeError) do + @cache.write('foo', 'bar', :expires_in => 'Mon Jun 29 13:10:40 -0700 2150') + end + assert_equal 'bat', @cache.read('baz') + assert_equal nil, @cache.read('foo') + end end class CompressedMemCacheStore < ActiveSupport::TestCase -- 1.6.4.2 From 0d3c5f0a822cd1b6029b5f619774b7794a94f370 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Sun, 28 Jun 2009 02:14:44 -0500 Subject: [PATCH 130/779] Patch FormTagHelper so that when a form tag is created, the div which holds the form authenticity token is set to display:inline [#2846 state:resolved] Signed-off-by: Yehuda Katz + Carl Lerche --- .../lib/action_view/helpers/form_tag_helper.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index c389307..c217191 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -450,10 +450,10 @@ module ActionView '' when /^post$/i, "", nil html_options["method"] = "post" - protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0') : '' + protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0;display:inline') : '' else html_options["method"] = "post" - content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0') + content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0;display:inline') end end -- 1.6.4.2 From f6f24b71a41f21953cf4c934a9ece6e1ed76285c Mon Sep 17 00:00:00 2001 From: J.D. Hollis Date: Tue, 30 Jun 2009 08:58:35 -0400 Subject: [PATCH 131/779] Only check for built extensions on gem dependencies that are in vendor/gems. [#2825 state:resolved] Signed-off-by: Yehuda Katz + Carl Lerche --- railties/lib/rails/gem_dependency.rb | 10 +++++++--- railties/test/gem_dependency_test.rb | 9 +++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb index 3cc7549..06d830b 100644 --- a/railties/lib/rails/gem_dependency.rb +++ b/railties/lib/rails/gem_dependency.rb @@ -122,10 +122,14 @@ module Rails def built? return false unless frozen? - specification.extensions.each do |ext| - makefile = File.join(unpacked_gem_directory, File.dirname(ext), 'Makefile') - return false unless File.exists?(makefile) + + if vendor_gem? + specification.extensions.each do |ext| + makefile = File.join(unpacked_gem_directory, File.dirname(ext), 'Makefile') + return false unless File.exists?(makefile) + end end + true end diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb index 58b664d..a4cf6f7 100644 --- a/railties/test/gem_dependency_test.rb +++ b/railties/test/gem_dependency_test.rb @@ -198,6 +198,15 @@ class GemDependencyTest < Test::Unit::TestCase assert_equal true, Rails::GemDependency.new("dummy-gem-i").built? assert_equal false, Rails::GemDependency.new("dummy-gem-j").built? end + + def test_gem_determines_build_status_only_on_vendor_gems + framework_gem = Rails::GemDependency.new('dummy-framework-gem') + framework_gem.stubs(:framework_gem?).returns(true) # already loaded + framework_gem.stubs(:vendor_rails?).returns(false) # but not in vendor/rails + framework_gem.stubs(:vendor_gem?).returns(false) # and not in vendor/gems + framework_gem.add_load_paths # freeze framework gem early + assert framework_gem.built? + end def test_gem_build_passes_options_to_dependencies start_gem = Rails::GemDependency.new("dummy-gem-g") -- 1.6.4.2 From 281c1a82deada82d5469e2aaa71485bbf7b5ec0d Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Thu, 2 Jul 2009 10:50:25 -0700 Subject: [PATCH 132/779] Fixes a number of tests that inexplicably didn't fail when we committed the original patch --- .../test/template/active_record_helper_test.rb | 2 +- actionpack/test/template/form_helper_test.rb | 10 +++++----- actionpack/test/template/form_tag_helper_test.rb | 6 +++--- actionpack/test/template/prototype_helper_test.rb | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index 83c028b..11812e7 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -170,7 +170,7 @@ class ActiveRecordHelperTest < ActionView::TestCase @request_forgery_protection_token = 'authenticity_token' @form_authenticity_token = '123' assert_dom_equal( - %(


\n


), + %(


\n


), form("post") ) end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 81c5cb3..5edd647 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -382,7 +382,7 @@ class FormHelperTest < ActionView::TestCase expected = "
" + - "
" + + "
" + "" + "" + "" + @@ -1091,7 +1091,7 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_existing_object form_for(@post) do |f| end - expected = "
" + expected = "
" assert_equal expected, output_buffer end @@ -1112,7 +1112,7 @@ class FormHelperTest < ActionView::TestCase form_for([@post, @comment]) {} - expected = %(
) + expected = %(
) assert_dom_equal expected, output_buffer end @@ -1131,7 +1131,7 @@ class FormHelperTest < ActionView::TestCase form_for([:admin, @post, @comment]) {} - expected = %(
) + expected = %(
) assert_dom_equal expected, output_buffer end @@ -1147,7 +1147,7 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_existing_object_and_custom_url form_for(@post, :url => "/super_posts") do |f| end - expected = "
" + expected = "
" assert_equal expected, output_buffer end diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 2d988cd..09a0c64 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -39,13 +39,13 @@ class FormTagHelperTest < ActionView::TestCase def test_form_tag_with_method_put actual = form_tag({}, { :method => :put }) - expected = %(
) + expected = %(
) assert_dom_equal expected, actual end def test_form_tag_with_method_delete actual = form_tag({}, { :method => :delete }) - expected = %(
) + expected = %(
) assert_dom_equal expected, actual end @@ -61,7 +61,7 @@ class FormTagHelperTest < ActionView::TestCase __in_erb_template = '' form_tag("http://example.com", :method => :put) { concat "Hello world!" } - expected = %(
Hello world!
) + expected = %(
Hello world!
) assert_dom_equal expected, output_buffer end diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 2ef4cde..3bcf532 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -130,7 +130,7 @@ class PrototypeHelperTest < PrototypeHelperBaseTest end def test_form_remote_tag_with_method - assert_dom_equal %(
), + assert_dom_equal %(
), form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :html => { :method => :put }) end @@ -158,7 +158,7 @@ class PrototypeHelperTest < PrototypeHelperBaseTest @record.save remote_form_for(@record) {} - expected = %(
) + expected = %(
) assert_dom_equal expected, output_buffer end @@ -174,7 +174,7 @@ class PrototypeHelperTest < PrototypeHelperBaseTest @article.save remote_form_for([@author, @article]) {} - expected = %(
) + expected = %(
) assert_dom_equal expected, output_buffer end -- 1.6.4.2 From d8fff7d9d5ac4283fcabd7ecf64af5709410424f Mon Sep 17 00:00:00 2001 From: Levin Alexander Date: Thu, 25 Jun 2009 22:47:27 +0200 Subject: [PATCH 133/779] make #inspect if zero length duration return '0 seconds' instead of empty string [#2838 state:resolved] Signed-off-by: Yehuda Katz + Carl Lerche --- activesupport/lib/active_support/duration.rb | 6 ++++-- activesupport/test/core_ext/duration_test.rb | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index f64661c..aa6d1aa 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -67,10 +67,12 @@ module ActiveSupport def inspect #:nodoc: consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h } - [:years, :months, :days, :minutes, :seconds].map do |length| + parts = [:years, :months, :days, :minutes, :seconds].map do |length| n = consolidated[length] "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero? - end.compact.to_sentence(:locale => :en) + end.compact + parts = ["0 seconds"] if parts.empty? + parts.to_sentence(:locale => :en) end protected diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 3a71779..e33eeb7 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -2,6 +2,7 @@ require 'abstract_unit' class DurationTest < ActiveSupport::TestCase def test_inspect + assert_equal '0 seconds', 0.seconds.inspect assert_equal '1 month', 1.month.inspect assert_equal '1 month and 1 day', (1.month + 1.day).inspect assert_equal '6 months and -2 days', (6.months - 2.days).inspect -- 1.6.4.2 From 1c855ad4e7c14fbf08edc1f1eb9b3c6fe186b701 Mon Sep 17 00:00:00 2001 From: Jarl Friis Date: Mon, 11 May 2009 14:09:22 +0200 Subject: [PATCH 134/779] My suggestion to fix ticket 2401 [#2401 state:resolved] Signed-off-by: Yehuda Katz + Carl Lerche --- actionpack/lib/action_view/helpers/form_helper.rb | 11 ++- actionpack/test/template/form_helper_test.rb | 94 ++++++++++++++++++++- 2 files changed, 99 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 6d30182..2ac407c 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -917,6 +917,7 @@ module ActionView attr_accessor :object_name, :object, :options def initialize(object_name, object, template, options, proc) + @nested_child_index = {} @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc @default_options = @options ? @options.slice(:index) : {} if @object_name.to_s.match(/\[\]$/) @@ -1019,7 +1020,7 @@ module ActionView explicit_child_index = args.last[:child_index] if args.last.is_a?(Hash) children.map do |child| - fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index}]", child, args, block) + fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, args, block) end.join else fields_for_nested_model(name, explicit_object || association, args, block) @@ -1037,9 +1038,9 @@ module ActionView end end - def nested_child_index - @nested_child_index ||= -1 - @nested_child_index += 1 + def nested_child_index(name) + @nested_child_index[name] ||= -1 + @nested_child_index[name] += 1 end end end @@ -1048,4 +1049,4 @@ module ActionView cattr_accessor :default_form_builder self.default_form_builder = ::ActionView::Helpers::FormBuilder end -end \ No newline at end of file +end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 5edd647..e92f62d 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -21,6 +21,9 @@ silence_warnings do attr_accessor :comments def comments_attributes=(attributes); end + + attr_accessor :tags + def tags_attributes=(attributes); end end class Comment @@ -33,6 +36,50 @@ silence_warnings do def name @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" end + + attr_accessor :relevances + def relevances_attributes=(attributes); end + + end + + class Tag + attr_reader :id + attr_reader :post_id + def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end + def save; @id = 1; @post_id = 1 end + def new_record?; @id.nil? end + def to_param; @id; end + def value + @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" + end + + attr_accessor :relevances + def relevances_attributes=(attributes); end + + end + + class CommentRelevance + attr_reader :id + attr_reader :comment_id + def initialize(id = nil, comment_id = nil); @id, @comment_id = id, comment_id end + def save; @id = 1; @comment_id = 1 end + def new_record?; @id.nil? end + def to_param; @id; end + def value + @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" + end + end + + class TagRelevance + attr_reader :id + attr_reader :tag_id + def initialize(id = nil, tag_id = nil); @id, @tag_id = id, tag_id end + def save; @id = 1; @tag_id = 1 end + def new_record?; @id.nil? end + def to_param; @id; end + def value + @id.nil? ? "new #{self.class.name.downcase}" : "#{self.class.name.downcase} ##{@id}" + end end class Author < Comment @@ -739,6 +786,51 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_uses_unique_indices_for_different_collection_associations + @post.comments = [Comment.new(321)] + @post.tags = [Tag.new(123), Tag.new(456)] + @post.comments[0].relevances = [] + @post.tags[0].relevances = [] + @post.tags[1].relevances = [] + form_for(:post, @post) do |f| + f.fields_for(:comments, @post.comments[0]) do |cf| + concat cf.text_field(:name) + cf.fields_for(:relevances, CommentRelevance.new(314)) do |crf| + concat crf.text_field(:value) + end + end + f.fields_for(:tags, @post.tags[0]) do |tf| + concat tf.text_field(:value) + tf.fields_for(:relevances, TagRelevance.new(3141)) do |trf| + concat trf.text_field(:value) + end + end + f.fields_for('tags', @post.tags[1]) do |tf| + concat tf.text_field(:value) + tf.fields_for(:relevances, TagRelevance.new(31415)) do |trf| + concat trf.text_field(:value) + end + end + end + + expected = '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
' + + assert_dom_equal expected, output_buffer + end + def test_fields_for fields_for(:post, @post) do |f| concat f.text_field(:title) @@ -1192,4 +1284,4 @@ class FormHelperTest < ActionView::TestCase def protect_against_forgery? false end -end \ No newline at end of file +end -- 1.6.4.2 From 944f4fc7d2ee8fc54008dfda1014983334427edc Mon Sep 17 00:00:00 2001 From: Brian Abreu Date: Wed, 24 Jun 2009 10:51:20 -0700 Subject: [PATCH 135/779] Fixed ActiveSupport::OrderedHash::[] work identically to ::Hash::[] in ruby 1.8.7 [#2832 state:resolved] Signed-off-by: Yehuda Katz + Carl Lerche --- activesupport/lib/active_support/ordered_hash.rb | 22 +++++++++++++++--- activesupport/test/ordered_hash_test.rb | 25 +++++++++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 8d1c0f5..4324e40 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -12,11 +12,25 @@ module ActiveSupport def self.[](*args) ordered_hash = new - args.each_with_index { |val,ind| - # Only every second value is a key. - next if ind % 2 != 0 + + if (args.length == 1 && args.first.is_a?(Array)) + args.first.each do |key_value_pair| + next unless (key_value_pair.is_a?(Array)) + ordered_hash[key_value_pair[0]] = key_value_pair[1] + end + + return ordered_hash + end + + unless (args.size % 2 == 0) + raise ArgumentError.new("odd number of arguments for Hash") + end + + args.each_with_index do |val, ind| + next if (ind % 2 != 0) ordered_hash[val] = args[ind + 1] - } + end + ordered_hash end diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 647938d..15bd571 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -163,9 +163,32 @@ class OrderedHashTest < Test::Unit::TestCase assert @ordered_hash.inspect.include?(@hash.inspect) end - def test_alternate_initialization + def test_alternate_initialization_with_splat alternate = ActiveSupport::OrderedHash[1,2,3,4] assert_kind_of ActiveSupport::OrderedHash, alternate assert_equal [1, 3], alternate.keys end + + def test_alternate_initialization_with_array + alternate = ActiveSupport::OrderedHash[ [ + [1, 2], + [3, 4], + "bad key value pair", + [ 'missing value' ] + ]] + + assert_kind_of ActiveSupport::OrderedHash, alternate + assert_equal [1, 3, 'missing value'], alternate.keys + assert_equal [2, 4, nil ], alternate.values + end + + def test_alternate_initialization_raises_exception_on_odd_length_args + begin + alternate = ActiveSupport::OrderedHash[1,2,3,4,5] + flunk "Hash::[] should have raised an exception on initialization " + + "with an odd number of parameters" + rescue + assert_equal "odd number of arguments for Hash", $!.message + end + end end -- 1.6.4.2 From 6673001a5e14ef2d342a03ae16f1da481bc01084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Garc=C3=ADa=20S=C3=A1ez?= Date: Tue, 23 Jun 2009 22:54:32 +0200 Subject: [PATCH 136/779] Allow symbols on routes declaration (:controller and :action values) [#2828 state:resolved] Signed-off-by: Yehuda Katz + Carl Lerche --- .../lib/action_controller/routing/route_set.rb | 1 + actionpack/test/controller/routing_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 397f06b..a983d37 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -305,6 +305,7 @@ module ActionController end def add_route(path, options = {}) + options.each { |k, v| options[k] = v.to_s if [:controller, :action].include?(k) && v.is_a?(Symbol) } route = builder.build(path, options) routes << route route diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index c07f508..9375942 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -2487,6 +2487,16 @@ class RouteSetTest < Test::Unit::TestCase end assert_equal({:controller => 'pages', :action => 'show', :name => 'JAMIS'}, set.recognize_path('/page/JAMIS')) end + + def test_routes_with_symbols + set.draw do |map| + map.connect 'unnamed', :controller => :pages, :action => :show, :name => :as_symbol + map.named 'named', :controller => :pages, :action => :show, :name => :as_symbol + end + assert_equal({:controller => 'pages', :action => 'show', :name => :as_symbol}, set.recognize_path('/unnamed')) + assert_equal({:controller => 'pages', :action => 'show', :name => :as_symbol}, set.recognize_path('/named')) + end + end class RouteLoadingTest < Test::Unit::TestCase -- 1.6.4.2 From 31254bedae669cd82a830571771749b74aaf3e5c Mon Sep 17 00:00:00 2001 From: Hongli Lai (Phusion) Date: Mon, 6 Jul 2009 14:14:08 +0200 Subject: [PATCH 137/779] Mocha >= 0.9.7 is required, otherwise mocking doesn't work. [#2874 state:resolved] Signed-off-by: Yehuda Katz --- actionpack/test/abstract_unit.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index cdeee93..0b67e56 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -8,7 +8,7 @@ require 'yaml' require 'stringio' require 'test/unit' -gem 'mocha', '>= 0.9.5' +gem 'mocha', '>= 0.9.7' require 'mocha' begin -- 1.6.4.2 From d8f8066cd1d34487ecd6e9ed43033a5aaa2096cb Mon Sep 17 00:00:00 2001 From: Hongli Lai (Phusion) Date: Sun, 5 Jul 2009 09:59:25 +0200 Subject: [PATCH 138/779] Add support for dumping non-standard primary keys when using the SQLite3 adapter. Fix unit tests so that this feature is tested for all adapters. Signed-off-by: Yehuda Katz --- activerecord/lib/active_record/schema_dumper.rb | 6 +++++- activerecord/test/cases/schema_dumper_test.rb | 14 +++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 557a554..651cd36 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -78,11 +78,14 @@ HEADER begin tbl = StringIO.new + # first dump primary key column if @connection.respond_to?(:pk_and_sequence_for) pk, pk_seq = @connection.pk_and_sequence_for(table) + elsif @connection.respond_to?(:primary_key) + pk = @connection.primary_key(table) end pk ||= 'id' - + tbl.print " create_table #{table.inspect}" if columns.detect { |c| c.name == pk } if pk != 'id' @@ -94,6 +97,7 @@ HEADER tbl.print ", :force => true" tbl.puts " do |t|" + # then dump all non-primary key columns column_specs = columns.map do |column| raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil? next if column.name == pk diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index f9ad7f3..972700d 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -156,6 +156,13 @@ class SchemaDumperTest < ActiveRecord::TestCase index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip assert_equal 'add_index "companies", ["firm_id", "type", "rating", "ruby_type"], :name => "company_index"', index_definition end + + def test_schema_dump_should_honor_nonstandard_primary_keys + output = standard_dump + match = output.match(%r{create_table "movies"(.*)do}) + assert_not_nil(match, "nonstandardpk table not found") + assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved" + end if current_adapter?(:MysqlAdapter) def test_schema_dump_should_not_add_default_value_for_mysql_text_field @@ -163,13 +170,6 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r{t.text\s+"body",\s+:null => false$}, output end - def test_mysql_schema_dump_should_honor_nonstandard_primary_keys - output = standard_dump - match = output.match(%r{create_table "movies"(.*)do}) - assert_not_nil(match, "nonstandardpk table not found") - assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved" - end - def test_schema_dump_includes_length_for_mysql_blob_and_text_fields output = standard_dump assert_match %r{t.binary\s+"tiny_blob",\s+:limit => 255$}, output -- 1.6.4.2 From 29c59858492d44cc737e1f0b944a51fe867261f8 Mon Sep 17 00:00:00 2001 From: Lawrence Pit Date: Mon, 6 Jul 2009 11:42:41 +1000 Subject: [PATCH 139/779] Use the i18n gem if present instead of vendor code. [#2871 state:resolved] Signed-off-by: Yehuda Katz --- activesupport/lib/active_support/vendor.rb | 9 ++++----- 1 files changed, 4 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index ebc62f9..5b51af0 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -20,10 +20,9 @@ rescue Gem::LoadError $:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.12" end -# TODO I18n gem has not been released yet -# begin -# gem 'i18n', '~> 0.1.3' -# rescue Gem::LoadError +begin + gem 'i18n', '~> 0.1.3' +rescue Gem::LoadError $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.1.3/lib" require 'i18n' -# end +end -- 1.6.4.2 From bc2c4a45959be21e6314fba7876b32c1f04cd08a Mon Sep 17 00:00:00 2001 From: Hongli Lai (Phusion) Date: Mon, 6 Jul 2009 14:11:28 +0200 Subject: [PATCH 140/779] Cleanup application after #close has been called on the Rack response body, not when AC::Reload#call is done. The Rack body might lazily evaluate its output, which is for example the case if one calls 'render :text => lambda { ... }'. The code which lazily evaluates the output might use other application classes. So we will want to defer cleanup until the Rack request is completely finished. Signed-off-by: Michael Koziarski --- actionpack/lib/action_controller/reloader.rb | 37 +++++++++- actionpack/test/controller/reloader_test.rb | 97 ++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 actionpack/test/controller/reloader_test.rb diff --git a/actionpack/lib/action_controller/reloader.rb b/actionpack/lib/action_controller/reloader.rb index 4678930..459dece 100644 --- a/actionpack/lib/action_controller/reloader.rb +++ b/actionpack/lib/action_controller/reloader.rb @@ -1,14 +1,45 @@ module ActionController class Reloader + class BodyWrapper + def initialize(body) + @body = body + end + + def close + @body.close if @body.respond_to?(:close) + ensure + Dispatcher.cleanup_application + end + + def method_missing(*args, &block) + @body.send(*args, &block) + end + + def respond_to?(symbol, include_private = false) + symbol == :close || @body.respond_to?(symbol, include_private) + end + end + def initialize(app) @app = app end def call(env) Dispatcher.reload_application - @app.call(env) - ensure - Dispatcher.cleanup_application + status, headers, body = @app.call(env) + # We do not want to call 'cleanup_application' in an ensure block + # because the returned Rack response body may lazily generate its data. This + # is for example the case if one calls + # + # render :text => lambda { ... code here which refers to application models ... } + # + # in an ActionController. + # + # Instead, we will want to cleanup the application code after the request is + # completely finished. So we wrap the body in a BodyWrapper class so that + # when the Rack handler calls #close during the end of the request, we get to + # run our cleanup code. + [status, headers, BodyWrapper.new(body)] end end end diff --git a/actionpack/test/controller/reloader_test.rb b/actionpack/test/controller/reloader_test.rb new file mode 100644 index 0000000..6f1f3c8 --- /dev/null +++ b/actionpack/test/controller/reloader_test.rb @@ -0,0 +1,97 @@ +require 'abstract_unit' + +class ReloaderTests < ActiveSupport::TestCase + Reloader = ActionController::Reloader + Dispatcher = ActionController::Dispatcher + + class MyBody < Array + def initialize(&block) + @on_close = block + end + + def foo + "foo" + end + + def bar + "bar" + end + + def close + @on_close.call if @on_close + end + end + + def setup_and_return_body(app = lambda { }) + Dispatcher.expects(:reload_application) + reloader = Reloader.new(app) + headers, status, body = reloader.call({ }) + body + end + + def test_it_reloads_the_application_before_the_request + Dispatcher.expects(:reload_application) + reloader = Reloader.new(lambda { + [200, { "Content-Type" => "text/html" }, [""]] + }) + reloader.call({ }) + end + + def test_returned_body_object_always_responds_to_close + body = setup_and_return_body(lambda { + [200, { "Content-Type" => "text/html" }, [""]] + }) + assert body.respond_to?(:close) + end + + def test_returned_body_object_behaves_like_underlying_object + body = setup_and_return_body(lambda { + b = MyBody.new + b << "hello" + b << "world" + [200, { "Content-Type" => "text/html" }, b] + }) + assert_equal 2, body.size + assert_equal "hello", body[0] + assert_equal "world", body[1] + assert_equal "foo", body.foo + assert_equal "bar", body.bar + end + + def test_it_calls_close_on_underlying_object_when_close_is_called_on_body + close_called = false + body = setup_and_return_body(lambda { + b = MyBody.new do + close_called = true + end + [200, { "Content-Type" => "text/html" }, b] + }) + body.close + assert close_called + end + + def test_returned_body_object_responds_to_all_methods_supported_by_underlying_object + body = setup_and_return_body(lambda { + [200, { "Content-Type" => "text/html" }, MyBody.new] + }) + assert body.respond_to?(:size) + assert body.respond_to?(:each) + assert body.respond_to?(:foo) + assert body.respond_to?(:bar) + end + + def test_it_doesnt_clean_up_the_application_after_call + Dispatcher.expects(:cleanup_application).never + body = setup_and_return_body(lambda { + [200, { "Content-Type" => "text/html" }, MyBody.new] + }) + end + + def test_it_cleans_up_the_application_when_close_is_called_on_body + Dispatcher.expects(:cleanup_application) + body = setup_and_return_body(lambda { + [200, { "Content-Type" => "text/html" }, MyBody.new] + }) + body.close + end +end -- 1.6.4.2 From 6a50d6c8e39bcc25df3d93226006b4523f8a2cb6 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 8 Jul 2009 17:30:33 +1200 Subject: [PATCH 141/779] Match tests with new reloading behaviour --- actionpack/test/controller/dispatcher_test.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index df9c961..3aa5bf9 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -25,7 +25,8 @@ class DispatcherTest < Test::Unit::TestCase def test_clears_dependencies_after_dispatch_if_in_loading_mode ActiveSupport::Dependencies.expects(:clear).once - dispatch(false) + # Close the response so dependencies kicks in + dispatch(false).last.close end def test_reloads_routes_before_dispatch_if_in_loading_mode -- 1.6.4.2 From 18e68d95240df673dc62af240a1eb63019a371f7 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 12 Jul 2009 15:43:39 +1200 Subject: [PATCH 142/779] Prepare version numbers, changelogs and gem dependencies for 2.3.3. --- actionmailer/CHANGELOG | 4 ++++ actionmailer/Rakefile | 2 +- actionmailer/lib/action_mailer/version.rb | 2 +- actionpack/CHANGELOG | 2 +- actionpack/Rakefile | 2 +- actionpack/lib/action_pack/version.rb | 2 +- activerecord/CHANGELOG | 2 +- activerecord/Rakefile | 2 +- activerecord/lib/active_record/version.rb | 2 +- activeresource/CHANGELOG | 4 ++++ activeresource/Rakefile | 2 +- activeresource/lib/active_resource/version.rb | 2 +- activesupport/CHANGELOG | 2 +- activesupport/lib/active_support/version.rb | 2 +- 14 files changed, 20 insertions(+), 12 deletions(-) diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 773e603..4090dd8 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,3 +1,7 @@ +*2.3.3 (July 12, 2009)* + +* No changes, just a version bump. + *2.3.2 [Final] (March 15, 2009)* * Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 [Matt Jones] diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index 83e3f22..e6dc259 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -54,7 +54,7 @@ spec = Gem::Specification.new do |s| s.rubyforge_project = "actionmailer" s.homepage = "http://www.rubyonrails.org" - s.add_dependency('actionpack', '= 2.3.2' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.3' + PKG_BUILD) s.has_rdoc = true s.requirements << 'none' diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index 08ff0d2..db86a29 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -2,7 +2,7 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 2 + TINY = 3 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 2e459fc..5f6fec6 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,4 +1,4 @@ -*2.3.3 (pending)* +*2.3.3 (July 12, 2009)* * Fixed that TestResponse.cookies was returning cookies unescaped #1867 [Doug McInnes] diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 6217877..f43ca4d 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -79,7 +79,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.3.2' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'action_controller' diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index e0aa2a5..66fca7d 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -2,7 +2,7 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 2 + TINY = 3 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index c577706..d820b2c 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,4 @@ -*2.3.3 (pending)* +*2.3.3 (July 12, 2009)* * Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH] diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 5905c67..7855053 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -176,7 +176,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.2' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD) s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite" s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite" diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 852807b..cbd6958 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,7 +2,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 2 + TINY = 3 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index 6572934..1142416 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,3 +1,7 @@ +*2.3.3 (July 12, 2009)* + +* No changes, just a version bump. + *2.3.2 [Final] (March 15, 2009)* * Nothing new, just included in 2.3.2 diff --git a/activeresource/Rakefile b/activeresource/Rakefile index c3cde26..c6591cc 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -66,7 +66,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.2' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'active_resource' diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index 3df2555..8c68de7 100644 --- a/activeresource/lib/active_resource/version.rb +++ b/activeresource/lib/active_resource/version.rb @@ -2,7 +2,7 @@ module ActiveResource module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 2 + TINY = 3 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 9a15bf9..cb57790 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,4 +1,4 @@ -*2.3.3 (pending)* +*2.3.3 (July 12, 2009)* * JSON: +Object#to_json+ calls +as_json+ to coerce itself into something natively encodable like +Hash+, +Integer+, or +String+. Override +as_json+ instead of +to_json+ so you're JSON-library-agnostic. [Jeremy Kemper] diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 30f598a..24ce690 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -2,7 +2,7 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 2 + TINY = 3 STRING = [MAJOR, MINOR, TINY].join('.') end -- 1.6.4.2 From f36d9a67582a857720aecf835917748163baa5a6 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 12 Jul 2009 19:28:04 -0500 Subject: [PATCH 143/779] Go back to depending on Rack 1.0.x gem --- actionpack/lib/action_controller.rb | 8 +- .../action_controller/vendor/rack-1.0.x/rack.rb | 90 ---- .../vendor/rack-1.0.x/rack/adapter/camping.rb | 22 - .../rack-1.0.x/rack/auth/abstract/handler.rb | 37 -- .../rack-1.0.x/rack/auth/abstract/request.rb | 37 -- .../vendor/rack-1.0.x/rack/auth/basic.rb | 58 --- .../vendor/rack-1.0.x/rack/auth/digest/md5.rb | 124 ----- .../vendor/rack-1.0.x/rack/auth/digest/nonce.rb | 51 -- .../vendor/rack-1.0.x/rack/auth/digest/params.rb | 55 -- .../vendor/rack-1.0.x/rack/auth/digest/request.rb | 40 -- .../vendor/rack-1.0.x/rack/auth/openid.rb | 487 ------------------ .../vendor/rack-1.0.x/rack/builder.rb | 63 --- .../vendor/rack-1.0.x/rack/cascade.rb | 41 -- .../vendor/rack-1.0.x/rack/chunked.rb | 49 -- .../vendor/rack-1.0.x/rack/commonlogger.rb | 52 -- .../vendor/rack-1.0.x/rack/conditionalget.rb | 47 -- .../vendor/rack-1.0.x/rack/content_length.rb | 29 - .../vendor/rack-1.0.x/rack/content_type.rb | 23 - .../vendor/rack-1.0.x/rack/deflater.rb | 96 ---- .../vendor/rack-1.0.x/rack/directory.rb | 153 ------ .../vendor/rack-1.0.x/rack/file.rb | 88 ---- .../vendor/rack-1.0.x/rack/handler.rb | 69 --- .../vendor/rack-1.0.x/rack/handler/cgi.rb | 61 --- .../rack-1.0.x/rack/handler/evented_mongrel.rb | 8 - .../vendor/rack-1.0.x/rack/handler/fastcgi.rb | 88 ---- .../vendor/rack-1.0.x/rack/handler/lsws.rb | 55 -- .../vendor/rack-1.0.x/rack/handler/mongrel.rb | 84 --- .../vendor/rack-1.0.x/rack/handler/scgi.rb | 59 --- .../rack-1.0.x/rack/handler/swiftiplied_mongrel.rb | 8 - .../vendor/rack-1.0.x/rack/handler/thin.rb | 18 - .../vendor/rack-1.0.x/rack/handler/webrick.rb | 68 --- .../vendor/rack-1.0.x/rack/head.rb | 19 - .../vendor/rack-1.0.x/rack/lint.rb | 537 -------------------- .../vendor/rack-1.0.x/rack/lobster.rb | 65 --- .../vendor/rack-1.0.x/rack/lock.rb | 16 - .../vendor/rack-1.0.x/rack/methodoverride.rb | 27 - .../vendor/rack-1.0.x/rack/mime.rb | 204 -------- .../vendor/rack-1.0.x/rack/mock.rb | 184 ------- .../vendor/rack-1.0.x/rack/recursive.rb | 57 -- .../vendor/rack-1.0.x/rack/reloader.rb | 106 ---- .../vendor/rack-1.0.x/rack/request.rb | 248 --------- .../vendor/rack-1.0.x/rack/response.rb | 183 ------- .../vendor/rack-1.0.x/rack/rewindable_input.rb | 98 ---- .../vendor/rack-1.0.x/rack/session/abstract/id.rb | 142 ----- .../vendor/rack-1.0.x/rack/session/cookie.rb | 91 ---- .../vendor/rack-1.0.x/rack/session/memcache.rb | 109 ---- .../vendor/rack-1.0.x/rack/session/pool.rb | 100 ---- .../vendor/rack-1.0.x/rack/showexceptions.rb | 349 ------------- .../vendor/rack-1.0.x/rack/showstatus.rb | 106 ---- .../vendor/rack-1.0.x/rack/static.rb | 38 -- .../vendor/rack-1.0.x/rack/urlmap.rb | 55 -- .../vendor/rack-1.0.x/rack/utils.rb | 520 ------------------- 52 files changed, 2 insertions(+), 5420 deletions(-) delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/adapter/camping.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/handler.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/request.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/basic.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/md5.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/nonce.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/params.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/request.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/openid.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/builder.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/cascade.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/chunked.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/commonlogger.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/conditionalget.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_length.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_type.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/deflater.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/directory.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/file.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/cgi.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/evented_mongrel.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/fastcgi.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/lsws.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/mongrel.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/scgi.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/swiftiplied_mongrel.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/thin.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/webrick.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/head.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lint.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lobster.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lock.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/methodoverride.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mime.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mock.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/recursive.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/reloader.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/request.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/response.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/rewindable_input.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/abstract/id.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/cookie.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/memcache.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/pool.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showexceptions.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showstatus.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/static.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/urlmap.rb delete mode 100644 actionpack/lib/action_controller/vendor/rack-1.0.x/rack/utils.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 91f843e..f53ba27 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -31,12 +31,8 @@ rescue LoadError end end -begin - gem 'rack', '~> 1.0.0' - require 'rack' -rescue Gem::LoadError - require 'action_controller/vendor/rack-1.0.x/rack' -end +gem 'rack', '~> 1.0.0' +require 'rack' module ActionController # TODO: Review explicit to see if they will automatically be handled by diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack.rb deleted file mode 100644 index 371d015..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack.rb +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (C) 2007, 2008, 2009 Christian Neukirchen -# -# Rack is freely distributable under the terms of an MIT-style license. -# See COPYING or http://www.opensource.org/licenses/mit-license.php. - -path = File.expand_path(File.dirname(__FILE__)) -$:.unshift(path) unless $:.include?(path) - - -# The Rack main module, serving as a namespace for all core Rack -# modules and classes. -# -# All modules meant for use in your application are autoloaded here, -# so it should be enough just to require rack.rb in your code. - -module Rack - # The Rack protocol version number implemented. - VERSION = [1,0] - - # Return the Rack protocol version as a dotted string. - def self.version - VERSION.join(".") - end - - # Return the Rack release as a dotted string. - def self.release - "1.0" - end - - autoload :Builder, "rack/builder" - autoload :Cascade, "rack/cascade" - autoload :Chunked, "rack/chunked" - autoload :CommonLogger, "rack/commonlogger" - autoload :ConditionalGet, "rack/conditionalget" - autoload :ContentLength, "rack/content_length" - autoload :ContentType, "rack/content_type" - autoload :File, "rack/file" - autoload :Deflater, "rack/deflater" - autoload :Directory, "rack/directory" - autoload :ForwardRequest, "rack/recursive" - autoload :Handler, "rack/handler" - autoload :Head, "rack/head" - autoload :Lint, "rack/lint" - autoload :Lock, "rack/lock" - autoload :MethodOverride, "rack/methodoverride" - autoload :Mime, "rack/mime" - autoload :Recursive, "rack/recursive" - autoload :Reloader, "rack/reloader" - autoload :ShowExceptions, "rack/showexceptions" - autoload :ShowStatus, "rack/showstatus" - autoload :Static, "rack/static" - autoload :URLMap, "rack/urlmap" - autoload :Utils, "rack/utils" - - autoload :MockRequest, "rack/mock" - autoload :MockResponse, "rack/mock" - - autoload :Request, "rack/request" - autoload :Response, "rack/response" - - module Auth - autoload :Basic, "rack/auth/basic" - autoload :AbstractRequest, "rack/auth/abstract/request" - autoload :AbstractHandler, "rack/auth/abstract/handler" - autoload :OpenID, "rack/auth/openid" - module Digest - autoload :MD5, "rack/auth/digest/md5" - autoload :Nonce, "rack/auth/digest/nonce" - autoload :Params, "rack/auth/digest/params" - autoload :Request, "rack/auth/digest/request" - end - end - - module Session - autoload :Cookie, "rack/session/cookie" - autoload :Pool, "rack/session/pool" - autoload :Memcache, "rack/session/memcache" - end - - # *Adapters* connect Rack with third party web frameworks. - # - # Rack includes an adapter for Camping, see README for other - # frameworks supporting Rack in their code bases. - # - # Refer to the submodules for framework-specific calling details. - - module Adapter - autoload :Camping, "rack/adapter/camping" - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/adapter/camping.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/adapter/camping.rb deleted file mode 100644 index 63bc787..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/adapter/camping.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Rack - module Adapter - class Camping - def initialize(app) - @app = app - end - - def call(env) - env["PATH_INFO"] ||= "" - env["SCRIPT_NAME"] ||= "" - controller = @app.run(env['rack.input'], env) - h = controller.headers - h.each_pair do |k,v| - if v.kind_of? URI - h[k] = v.to_s - end - end - [controller.status, controller.headers, [controller.body.to_s]] - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/handler.rb deleted file mode 100644 index 214df62..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/handler.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Rack - module Auth - # Rack::Auth::AbstractHandler implements common authentication functionality. - # - # +realm+ should be set for all handlers. - - class AbstractHandler - - attr_accessor :realm - - def initialize(app, realm=nil, &authenticator) - @app, @realm, @authenticator = app, realm, authenticator - end - - - private - - def unauthorized(www_authenticate = challenge) - return [ 401, - { 'Content-Type' => 'text/plain', - 'Content-Length' => '0', - 'WWW-Authenticate' => www_authenticate.to_s }, - [] - ] - end - - def bad_request - return [ 400, - { 'Content-Type' => 'text/plain', - 'Content-Length' => '0' }, - [] - ] - end - - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/request.rb deleted file mode 100644 index 1d9ccec..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/abstract/request.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Rack - module Auth - class AbstractRequest - - def initialize(env) - @env = env - end - - def provided? - !authorization_key.nil? - end - - def parts - @parts ||= @env[authorization_key].split(' ', 2) - end - - def scheme - @scheme ||= parts.first.downcase.to_sym - end - - def params - @params ||= parts.last - end - - - private - - AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION'] - - def authorization_key - @authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) } - end - - end - - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/basic.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/basic.rb deleted file mode 100644 index 9557224..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/basic.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'rack/auth/abstract/handler' -require 'rack/auth/abstract/request' - -module Rack - module Auth - # Rack::Auth::Basic implements HTTP Basic Authentication, as per RFC 2617. - # - # Initialize with the Rack application that you want protecting, - # and a block that checks if a username and password pair are valid. - # - # See also: example/protectedlobster.rb - - class Basic < AbstractHandler - - def call(env) - auth = Basic::Request.new(env) - - return unauthorized unless auth.provided? - - return bad_request unless auth.basic? - - if valid?(auth) - env['REMOTE_USER'] = auth.username - - return @app.call(env) - end - - unauthorized - end - - - private - - def challenge - 'Basic realm="%s"' % realm - end - - def valid?(auth) - @authenticator.call(*auth.credentials) - end - - class Request < Auth::AbstractRequest - def basic? - :basic == scheme - end - - def credentials - @credentials ||= params.unpack("m*").first.split(/:/, 2) - end - - def username - credentials.first - end - end - - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/md5.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/md5.rb deleted file mode 100644 index e579dc9..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/md5.rb +++ /dev/null @@ -1,124 +0,0 @@ -require 'rack/auth/abstract/handler' -require 'rack/auth/digest/request' -require 'rack/auth/digest/params' -require 'rack/auth/digest/nonce' -require 'digest/md5' - -module Rack - module Auth - module Digest - # Rack::Auth::Digest::MD5 implements the MD5 algorithm version of - # HTTP Digest Authentication, as per RFC 2617. - # - # Initialize with the [Rack] application that you want protecting, - # and a block that looks up a plaintext password for a given username. - # - # +opaque+ needs to be set to a constant base64/hexadecimal string. - # - class MD5 < AbstractHandler - - attr_accessor :opaque - - attr_writer :passwords_hashed - - def initialize(*args) - super - @passwords_hashed = nil - end - - def passwords_hashed? - !!@passwords_hashed - end - - def call(env) - auth = Request.new(env) - - unless auth.provided? - return unauthorized - end - - if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth) - return bad_request - end - - if valid?(auth) - if auth.nonce.stale? - return unauthorized(challenge(:stale => true)) - else - env['REMOTE_USER'] = auth.username - - return @app.call(env) - end - end - - unauthorized - end - - - private - - QOP = 'auth'.freeze - - def params(hash = {}) - Params.new do |params| - params['realm'] = realm - params['nonce'] = Nonce.new.to_s - params['opaque'] = H(opaque) - params['qop'] = QOP - - hash.each { |k, v| params[k] = v } - end - end - - def challenge(hash = {}) - "Digest #{params(hash)}" - end - - def valid?(auth) - valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth) - end - - def valid_qop?(auth) - QOP == auth.qop - end - - def valid_opaque?(auth) - H(opaque) == auth.opaque - end - - def valid_nonce?(auth) - auth.nonce.valid? - end - - def valid_digest?(auth) - digest(auth, @authenticator.call(auth.username)) == auth.response - end - - def md5(data) - ::Digest::MD5.hexdigest(data) - end - - alias :H :md5 - - def KD(secret, data) - H([secret, data] * ':') - end - - def A1(auth, password) - [ auth.username, auth.realm, password ] * ':' - end - - def A2(auth) - [ auth.method, auth.uri ] * ':' - end - - def digest(auth, password) - password_hash = passwords_hashed? ? password : H(A1(auth, password)) - - KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, QOP, H(A2(auth)) ] * ':') - end - - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/nonce.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/nonce.rb deleted file mode 100644 index dbe109f..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/nonce.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'digest/md5' - -module Rack - module Auth - module Digest - # Rack::Auth::Digest::Nonce is the default nonce generator for the - # Rack::Auth::Digest::MD5 authentication handler. - # - # +private_key+ needs to set to a constant string. - # - # +time_limit+ can be optionally set to an integer (number of seconds), - # to limit the validity of the generated nonces. - - class Nonce - - class << self - attr_accessor :private_key, :time_limit - end - - def self.parse(string) - new(*string.unpack("m*").first.split(' ', 2)) - end - - def initialize(timestamp = Time.now, given_digest = nil) - @timestamp, @given_digest = timestamp.to_i, given_digest - end - - def to_s - [([ @timestamp, digest ] * ' ')].pack("m*").strip - end - - def digest - ::Digest::MD5.hexdigest([ @timestamp, self.class.private_key ] * ':') - end - - def valid? - digest == @given_digest - end - - def stale? - !self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit - end - - def fresh? - !stale? - end - - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/params.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/params.rb deleted file mode 100644 index 730e2ef..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/params.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Rack - module Auth - module Digest - class Params < Hash - - def self.parse(str) - split_header_value(str).inject(new) do |header, param| - k, v = param.split('=', 2) - header[k] = dequote(v) - header - end - end - - def self.dequote(str) # From WEBrick::HTTPUtils - ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup - ret.gsub!(/\\(.)/, "\\1") - ret - end - - def self.split_header_value(str) - str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] } - end - - def initialize - super - - yield self if block_given? - end - - def [](k) - super k.to_s - end - - def []=(k, v) - super k.to_s, v.to_s - end - - UNQUOTED = ['qop', 'nc', 'stale'] - - def to_s - inject([]) do |parts, (k, v)| - parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v)) - parts - end.join(', ') - end - - def quote(str) # From WEBrick::HTTPUtils - '"' << str.gsub(/[\\\"]/o, "\\\1") << '"' - end - - end - end - end -end - diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/request.rb deleted file mode 100644 index a8aa3bf..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/digest/request.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'rack/auth/abstract/request' -require 'rack/auth/digest/params' -require 'rack/auth/digest/nonce' - -module Rack - module Auth - module Digest - class Request < Auth::AbstractRequest - - def method - @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD'] - end - - def digest? - :digest == scheme - end - - def correct_uri? - (@env['SCRIPT_NAME'].to_s + @env['PATH_INFO'].to_s) == uri - end - - def nonce - @nonce ||= Nonce.parse(params['nonce']) - end - - def params - @params ||= Params.parse(parts.last) - end - - def method_missing(sym) - if params.has_key? key = sym.to_s - return params[key] - end - super - end - - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/openid.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/openid.rb deleted file mode 100644 index 43cbe4f..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/auth/openid.rb +++ /dev/null @@ -1,487 +0,0 @@ -# AUTHOR: Scytrin dai Kinthra ; blink#ruby-lang@irc.freenode.net - -gem 'ruby-openid', '~> 2' if defined? Gem -require 'rack/request' -require 'rack/utils' -require 'rack/auth/abstract/handler' - -require 'uri' -require 'openid' -require 'openid/extension' -require 'openid/store/memory' - -module Rack - class Request - def openid_request - @env['rack.auth.openid.request'] - end - - def openid_response - @env['rack.auth.openid.response'] - end - end - - module Auth - - # Rack::Auth::OpenID provides a simple method for setting up an OpenID - # Consumer. It requires the ruby-openid library from janrain to operate, - # as well as a rack method of session management. - # - # The ruby-openid home page is at http://openidenabled.com/ruby-openid/. - # - # The OpenID specifications can be found at - # http://openid.net/specs/openid-authentication-1_1.html - # and - # http://openid.net/specs/openid-authentication-2_0.html. Documentation - # for published OpenID extensions and related topics can be found at - # http://openid.net/developers/specs/. - # - # It is recommended to read through the OpenID spec, as well as - # ruby-openid's documentation, to understand what exactly goes on. However - # a setup as simple as the presented examples is enough to provide - # Consumer functionality. - # - # This library strongly intends to utilize the OpenID 2.0 features of the - # ruby-openid library, which provides OpenID 1.0 compatiblity. - # - # NOTE: Due to the amount of data that this library stores in the - # session, Rack::Session::Cookie may fault. - # - # == Examples - # - # simple_oid = OpenID.new('http://mysite.com/') - # - # return_oid = OpenID.new('http://mysite.com/', { - # :return_to => 'http://mysite.com/openid' - # }) - # - # complex_oid = OpenID.new('http://mysite.com/', - # :immediate => true, - # :extensions => { - # ::OpenID::SReg => [['email'],['nickname']] - # } - # ) - # - # = Advanced - # - # Most of the functionality of this library is encapsulated such that - # expansion and overriding functions isn't difficult nor tricky. - # Alternately, to avoid opening up singleton objects or subclassing, a - # wrapper rack middleware can be composed to act upon Auth::OpenID's - # responses. See #check and #finish for locations of pertinent data. - # - # == Responses - # - # To change the responses that Auth::OpenID returns, override the methods - # #redirect, #bad_request, #unauthorized, #access_denied, and - # #foreign_server_failure. - # - # Additionally #confirm_post_params is used when the URI would exceed - # length limits on a GET request when doing the initial verification - # request. - # - # == Processing - # - # To change methods of processing completed transactions, override the - # methods #success, #setup_needed, #cancel, and #failure. Please ensure - # the returned object is a rack compatible response. - # - # The first argument is an OpenID::Response, the second is a - # Rack::Request of the current request, the last is the hash used in - # ruby-openid handling, which can be found manually at - # env['rack.session'][:openid]. - # - # This is useful if you wanted to expand the processing done, such as - # setting up user accounts. - # - # oid_app = Rack::Auth::OpenID.new realm, :return_to => return_to - # def oid_app.success oid, request, session - # user = Models::User[oid.identity_url] - # user ||= Models::User.create_from_openid oid - # request['rack.session'][:user] = user.id - # redirect MyApp.site_home - # end - # - # site_map['/openid'] = oid_app - # map = Rack::URLMap.new site_map - # ... - - class OpenID - # Raised if an incompatible session is being used. - class NoSession < RuntimeError; end - # Raised if an extension not matching specifications is provided. - class BadExtension < RuntimeError; end - # Possible statuses returned from consumer responses. See definitions - # in the ruby-openid library. - ValidStatus = [ - ::OpenID::Consumer::SUCCESS, - ::OpenID::Consumer::FAILURE, - ::OpenID::Consumer::CANCEL, - ::OpenID::Consumer::SETUP_NEEDED - ] - - # The first argument is the realm, identifying the site they are trusting - # with their identity. This is required, also treated as the trust_root - # in OpenID 1.x exchanges. - # - # The lits of acceptable options include :return_to, :session_key, - # :openid_param, :store, :immediate, :extensions. - # - # :return_to defines the url to return to after the client - # authenticates with the openid service provider. This url should point - # to where Rack::Auth::OpenID is mounted. If unprovided, the url of - # the current request is used. - # - # :session_key defines the key to the session hash in the env. - # The default is 'rack.session'. - # - # :openid_param defines at what key in the request parameters to - # find the identifier to resolve. As per the 2.0 spec, the default is - # 'openid_identifier'. - # - # :store defined what OpenID Store to use for persistant - # information. By default a Store::Memory is used. - # - # :immediate as true will make initial requests to be of an - # immediate type. This is false by default. See OpenID specification - # documentation. - # - # :extensions should be a hash of openid extension - # implementations. The key should be the extension module, the value - # should be an array of arguments for extension::Request.new(). - # The hash is iterated over and passed to #add_extension for processing. - # Please see #add_extension for further documentation. - - def initialize(realm, options={}) - realm = URI(realm) - raise ArgumentError, "Invalid realm: #{realm}" \ - unless realm.absolute? \ - and realm.fragment.nil? \ - and realm.scheme =~ /^https?$/ \ - and realm.host =~ /^(\*\.)?#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+/ - realm.path = '/' if realm.path.empty? - @realm = realm.to_s - - if ruri = options[:return_to] - ruri = URI(ruri) - raise ArgumentError, "Invalid return_to: #{ruri}" \ - unless ruri.absolute? \ - and ruri.scheme =~ /^https?$/ \ - and ruri.fragment.nil? - raise ArgumentError, "return_to #{ruri} not within realm #{realm}" \ - unless self.within_realm?(ruri) - @return_to = ruri.to_s - end - - @session_key = options[:session_key] || 'rack.session' - @openid_param = options[:openid_param] || 'openid_identifier' - @store = options[:store] || ::OpenID::Store::Memory.new - @immediate = !!options[:immediate] - - @extensions = {} - if extensions = options[:extensions] - extensions.each do |ext, args| - add_extension(ext, *args) - end - end - - # Undocumented, semi-experimental - @anonymous = !!options[:anonymous] - end - - attr_reader :realm, :return_to, :session_key, :openid_param, :store, - :immediate, :extensions - - # Sets up and uses session data at :openid within the session. - # Errors in this setup will raise a NoSession exception. - # - # If the parameter 'openid.mode' is set, which implies a followup from - # the openid server, processing is passed to #finish and the result is - # returned. However, if there is no appropriate openid information in the - # session, a 400 error is returned. - # - # If the parameter specified by options[:openid_param] is - # present, processing is passed to #check and the result is returned. - # - # If neither of these conditions are met, #bad_request is called. - - def call(env) - env['rack.auth.openid'] = self - env_session = env[@session_key] - unless env_session and env_session.is_a?(Hash) - raise NoSession, 'No compatible session.' - end - # let us work in our own namespace... - session = (env_session[:openid] ||= {}) - unless session and session.is_a?(Hash) - raise NoSession, 'Incompatible openid session.' - end - - request = Rack::Request.new(env) - consumer = ::OpenID::Consumer.new(session, @store) - - if mode = request.GET['openid.mode'] - finish(consumer, session, request) - elsif request.GET[@openid_param] - check(consumer, session, request) - else - bad_request - end - end - - # As the first part of OpenID consumer action, #check retrieves the data - # required for completion. - # - # If all parameters fit within the max length of a URI, a 303 redirect - # will be returned. Otherwise #confirm_post_params will be called. - # - # Any messages from OpenID's request are logged to env['rack.errors'] - # - # env['rack.auth.openid.request'] is the openid checkid request - # instance. - # - # session[:openid_param] is set to the openid identifier - # provided by the user. - # - # session[:return_to] is set to the return_to uri given to the - # identity provider. - - def check(consumer, session, req) - oid = consumer.begin(req.GET[@openid_param], @anonymous) - req.env['rack.auth.openid.request'] = oid - req.env['rack.errors'].puts(oid.message) - p oid if $DEBUG - - ## Extension support - extensions.each do |ext,args| - oid.add_extension(ext::Request.new(*args)) - end - - session[:openid_param] = req.GET[openid_param] - return_to_uri = return_to ? return_to : req.url - session[:return_to] = return_to_uri - immediate = session.key?(:setup_needed) ? false : immediate - - if oid.send_redirect?(realm, return_to_uri, immediate) - redirect(oid.redirect_url(realm, return_to_uri, immediate)) - else - confirm_post_params(oid, realm, return_to_uri, immediate) - end - rescue ::OpenID::DiscoveryFailure => e - # thrown from inside OpenID::Consumer#begin by yadis stuff - req.env['rack.errors'].puts( [e.message, *e.backtrace]*"\n" ) - return foreign_server_failure - end - - # This is the final portion of authentication. - # If successful, a redirect to the realm is be returned. - # Data gathered from extensions are stored in session[:openid] with the - # extension's namespace uri as the key. - # - # Any messages from OpenID's response are logged to env['rack.errors'] - # - # env['rack.auth.openid.response'] will contain the openid - # response. - - def finish(consumer, session, req) - oid = consumer.complete(req.GET, req.url) - req.env['rack.auth.openid.response'] = oid - req.env['rack.errors'].puts(oid.message) - p oid if $DEBUG - - if ValidStatus.include?(oid.status) - __send__(oid.status, oid, req, session) - else - invalid_status(oid, req, session) - end - end - - # The first argument should be the main extension module. - # The extension module should contain the constants: - # * class Request, should have OpenID::Extension as an ancestor - # * class Response, should have OpenID::Extension as an ancestor - # * string NS_URI, which defining the namespace of the extension - # - # All trailing arguments will be passed to extension::Request.new in - # #check. - # The openid response will be passed to - # extension::Response#from_success_response, oid#get_extension_args will - # be called on the result to attain the gathered data. - # - # This method returns the key at which the response data will be found in - # the session, which is the namespace uri by default. - - def add_extension(ext, *args) - raise BadExtension unless valid_extension?(ext) - extensions[ext] = args - return ext::NS_URI - end - - # Checks the validitity, in the context of usage, of a submitted - # extension. - - def valid_extension?(ext) - if not %w[NS_URI Request Response].all?{|c| ext.const_defined?(c) } - raise ArgumentError, 'Extension is missing constants.' - elsif not ext::Response.respond_to?(:from_success_response) - raise ArgumentError, 'Response is missing required method.' - end - return true - rescue - return false - end - - # Checks the provided uri to ensure it'd be considered within the realm. - # is currently not compatible with wildcard realms. - - def within_realm? uri - uri = URI.parse(uri.to_s) - realm = URI.parse(self.realm) - return false unless uri.absolute? - return false unless uri.path[0, realm.path.size] == realm.path - return false unless uri.host == realm.host or realm.host[/^\*\./] - # for wildcard support, is awkward with URI limitations - realm_match = Regexp.escape(realm.host). - sub(/^\*\./,"^#{URI::REGEXP::PATTERN::URIC_NO_SLASH}+.")+'$' - return false unless uri.host.match(realm_match) - return true - end - - alias_method :include?, :within_realm? - - protected - - # Returns an html form page for posting to an Identity Provider if the - # GET request would exceed the upper URI length limit. - - def confirm_post_params(oid, realm, return_to, immediate) - response = Rack::Response.new ''+ - 'Confirm...'+ - ''+oid.form_markup(realm, return_to, immediate)+''+ - '' - response.finish - end - - # Returns a 303 redirect with the destination of that provided by the - # argument. - - def redirect(uri) - [ 303, {'Content-Type'=>'text/plain', 'Content-Length'=>'0', - 'Location' => uri}, - [] ] - end - - # Returns an empty 400 response. - - def bad_request - [ 400, {'Content-Type'=>'text/plain', 'Content-Length'=>'0'}, - [''] ] - end - - # Returns a basic unauthorized 401 response. - - def unauthorized - [ 401, {'Content-Type' => 'text/plain', 'Content-Length' => '13'}, - ['Unauthorized.'] ] - end - - # Returns a basic access denied 403 response. - - def access_denied - [ 403, {'Content-Type' => 'text/plain', 'Content-Length' => '14'}, - ['Access denied.'] ] - end - - # Returns a 503 response to be used if communication with the remote - # OpenID server fails. - - def foreign_server_failure - [ 503, {'Content-Type'=>'text/plain', 'Content-Length' => '23'}, - ['Foreign server failure.'] ] - end - - private - - # Called to complete processing on a successful transaction. - # Within the openid session, :openid_identity and :openid_identifier are - # set to the user friendly and the standard representation of the - # validated identity. All other data in the openid session is cleared. - - def success(oid, request, session) - session.clear - session[:openid_identity] = oid.display_identifier - session[:openid_identifier] = oid.identity_url - extensions.keys.each do |ext| - label = ext.name[/[^:]+$/].downcase - response = ext::Response.from_success_response(oid) - session[label] = response.data - end - redirect(realm) - end - - # Called if the Identity Provider indicates further setup by the user is - # required. - # The identifier is retrived from the openid session at :openid_param. - # And :setup_needed is set to true to prevent looping. - - def setup_needed(oid, request, session) - identifier = session[:openid_param] - session[:setup_needed] = true - redirect(req.script_name + '?' + openid_param + '=' + identifier) - end - - # Called if the user indicates they wish to cancel identification. - # Data within openid session is cleared. - - def cancel(oid, request, session) - session.clear - access_denied - end - - # Called if the Identity Provider indicates the user is unable to confirm - # their identity. Data within the openid session is left alone, in case - # of swarm auth attacks. - - def failure(oid, request, session) - unauthorized - end - - # To be called if there is no method for handling the OpenID response - # status. - - def invalid_status(oid, request, session) - msg = 'Invalid status returned by the OpenID authorization reponse.' - [ 500, - {'Content-Type'=>'text/plain','Content-Length'=>msg.length.to_s}, - [msg] ] - end - end - - # A class developed out of the request to use OpenID as an authentication - # middleware. The request will be sent to the OpenID instance unless the - # block evaluates to true. For example in rackup, you can use it as such: - # - # use Rack::Session::Pool - # use Rack::Auth::OpenIDAuth, realm, openid_options do |env| - # env['rack.session'][:authkey] == a_string - # end - # run RackApp - # - # Or simply: - # - # app = Rack::Auth::OpenIDAuth.new app, realm, openid_options, &auth - - class OpenIDAuth < Rack::Auth::AbstractHandler - attr_reader :oid - def initialize(app, realm, options={}, &auth) - @oid = OpenID.new(realm, options) - super(app, &auth) - end - - def call(env) - to = @authenticator.call(env) ? @app : @oid - to.call(env) - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/builder.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/builder.rb deleted file mode 100644 index 295235e..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/builder.rb +++ /dev/null @@ -1,63 +0,0 @@ -module Rack - # Rack::Builder implements a small DSL to iteratively construct Rack - # applications. - # - # Example: - # - # app = Rack::Builder.new { - # use Rack::CommonLogger - # use Rack::ShowExceptions - # map "/lobster" do - # use Rack::Lint - # run Rack::Lobster.new - # end - # } - # - # Or - # - # app = Rack::Builder.app do - # use Rack::CommonLogger - # lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] } - # end - # - # +use+ adds a middleware to the stack, +run+ dispatches to an application. - # You can use +map+ to construct a Rack::URLMap in a convenient way. - - class Builder - def initialize(&block) - @ins = [] - instance_eval(&block) if block_given? - end - - def self.app(&block) - self.new(&block).to_app - end - - def use(middleware, *args, &block) - @ins << lambda { |app| middleware.new(app, *args, &block) } - end - - def run(app) - @ins << app #lambda { |nothing| app } - end - - def map(path, &block) - if @ins.last.kind_of? Hash - @ins.last[path] = self.class.new(&block).to_app - else - @ins << {} - map(path, &block) - end - end - - def to_app - @ins[-1] = Rack::URLMap.new(@ins.last) if Hash === @ins.last - inner_app = @ins.last - @ins[0...-1].reverse.inject(inner_app) { |a, e| e.call(a) } - end - - def call(env) - to_app.call(env) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/cascade.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/cascade.rb deleted file mode 100644 index 14c3e54..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/cascade.rb +++ /dev/null @@ -1,41 +0,0 @@ -module Rack - # Rack::Cascade tries an request on several apps, and returns the - # first response that is not 404 (or in a list of configurable - # status codes). - - class Cascade - NotFound = [404, {}, []] - - attr_reader :apps - - def initialize(apps, catch=404) - @apps = []; @has_app = {} - apps.each { |app| add app } - - @catch = {} - [*catch].each { |status| @catch[status] = true } - end - - def call(env) - result = NotFound - - @apps.each do |app| - result = app.call(env) - break unless @catch.include?(result[0].to_i) - end - - result - end - - def add app - @has_app[app] = true - @apps << app - end - - def include? app - @has_app.include? app - end - - alias_method :<<, :add - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/chunked.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/chunked.rb deleted file mode 100644 index 280d89d..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/chunked.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'rack/utils' - -module Rack - - # Middleware that applies chunked transfer encoding to response bodies - # when the response does not include a Content-Length header. - class Chunked - include Rack::Utils - - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = HeaderHash.new(headers) - - if env['HTTP_VERSION'] == 'HTTP/1.0' || - STATUS_WITH_NO_ENTITY_BODY.include?(status) || - headers['Content-Length'] || - headers['Transfer-Encoding'] - [status, headers.to_hash, body] - else - dup.chunk(status, headers, body) - end - end - - def chunk(status, headers, body) - @body = body - headers.delete('Content-Length') - headers['Transfer-Encoding'] = 'chunked' - [status, headers.to_hash, self] - end - - def each - term = "\r\n" - @body.each do |chunk| - size = bytesize(chunk) - next if size == 0 - yield [size.to_s(16), term, chunk, term].join - end - yield ["0", term, "", term].join - end - - def close - @body.close if @body.respond_to?(:close) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/commonlogger.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/commonlogger.rb deleted file mode 100644 index 880f0fb..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/commonlogger.rb +++ /dev/null @@ -1,52 +0,0 @@ -module Rack - # Rack::CommonLogger forwards every request to an +app+ given, and - # logs a line in the Apache common log format to the +logger+, or - # rack.errors by default. - class CommonLogger - # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common - # lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 - - # %{%s - %s [%s] "%s %s%s %s" %d %s\n} % - FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} - - def initialize(app, logger=nil) - @app = app - @logger = logger - end - - def call(env) - began_at = Time.now - status, header, body = @app.call(env) - log(env, status, header, began_at) - [status, header, body] - end - - private - - def log(env, status, header, began_at) - now = Time.now - length = extract_content_length(header) - - logger = @logger || env['rack.errors'] - logger.write FORMAT % [ - env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-", - env["REMOTE_USER"] || "-", - now.strftime("%d/%b/%Y %H:%M:%S"), - env["REQUEST_METHOD"], - env["PATH_INFO"], - env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"], - env["HTTP_VERSION"], - status.to_s[0..3], - length, - now - began_at ] - end - - def extract_content_length(headers) - headers.each do |key, value| - if key.downcase == 'content-length' - return value.to_s == '0' ? '-' : value - end - end - '-' - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/conditionalget.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/conditionalget.rb deleted file mode 100644 index 046ebdb..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/conditionalget.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'rack/utils' - -module Rack - - # Middleware that enables conditional GET using If-None-Match and - # If-Modified-Since. The application should set either or both of the - # Last-Modified or Etag response headers according to RFC 2616. When - # either of the conditions is met, the response body is set to be zero - # length and the response status is set to 304 Not Modified. - # - # Applications that defer response body generation until the body's each - # message is received will avoid response body generation completely when - # a conditional GET matches. - # - # Adapted from Michael Klishin's Merb implementation: - # http://github.com/wycats/merb-core/tree/master/lib/merb-core/rack/middleware/conditional_get.rb - class ConditionalGet - def initialize(app) - @app = app - end - - def call(env) - return @app.call(env) unless %w[GET HEAD].include?(env['REQUEST_METHOD']) - - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - if etag_matches?(env, headers) || modified_since?(env, headers) - status = 304 - headers.delete('Content-Type') - headers.delete('Content-Length') - body = [] - end - [status, headers, body] - end - - private - def etag_matches?(env, headers) - etag = headers['Etag'] and etag == env['HTTP_IF_NONE_MATCH'] - end - - def modified_since?(env, headers) - last_modified = headers['Last-Modified'] and - last_modified == env['HTTP_IF_MODIFIED_SINCE'] - end - end - -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_length.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_length.rb deleted file mode 100644 index 1e56d43..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_length.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'rack/utils' - -module Rack - # Sets the Content-Length header on responses with fixed-length bodies. - class ContentLength - include Rack::Utils - - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = HeaderHash.new(headers) - - if !STATUS_WITH_NO_ENTITY_BODY.include?(status) && - !headers['Content-Length'] && - !headers['Transfer-Encoding'] && - (body.respond_to?(:to_ary) || body.respond_to?(:to_str)) - - body = [body] if body.respond_to?(:to_str) # rack 0.4 compat - length = body.to_ary.inject(0) { |len, part| len + bytesize(part) } - headers['Content-Length'] = length.to_s - end - - [status, headers, body] - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_type.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_type.rb deleted file mode 100644 index 0c1e1ca..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/content_type.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'rack/utils' - -module Rack - - # Sets the Content-Type header on responses which don't have one. - # - # Builder Usage: - # use Rack::ContentType, "text/plain" - # - # When no content type argument is provided, "text/html" is assumed. - class ContentType - def initialize(app, content_type = "text/html") - @app, @content_type = app, content_type - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - headers['Content-Type'] ||= @content_type - [status, headers.to_hash, body] - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/deflater.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/deflater.rb deleted file mode 100644 index 14137a9..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/deflater.rb +++ /dev/null @@ -1,96 +0,0 @@ -require "zlib" -require "stringio" -require "time" # for Time.httpdate -require 'rack/utils' - -module Rack - class Deflater - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - - # Skip compressing empty entity body responses and responses with - # no-transform set. - if Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) || - headers['Cache-Control'].to_s =~ /\bno-transform\b/ - return [status, headers, body] - end - - request = Request.new(env) - - encoding = Utils.select_best_encoding(%w(gzip deflate identity), - request.accept_encoding) - - # Set the Vary HTTP header. - vary = headers["Vary"].to_s.split(",").map { |v| v.strip } - unless vary.include?("*") || vary.include?("Accept-Encoding") - headers["Vary"] = vary.push("Accept-Encoding").join(",") - end - - case encoding - when "gzip" - headers['Content-Encoding'] = "gzip" - headers.delete('Content-Length') - mtime = headers.key?("Last-Modified") ? - Time.httpdate(headers["Last-Modified"]) : Time.now - [status, headers, GzipStream.new(body, mtime)] - when "deflate" - headers['Content-Encoding'] = "deflate" - headers.delete('Content-Length') - [status, headers, DeflateStream.new(body)] - when "identity" - [status, headers, body] - when nil - message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found." - [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]] - end - end - - class GzipStream - def initialize(body, mtime) - @body = body - @mtime = mtime - end - - def each(&block) - @writer = block - gzip =::Zlib::GzipWriter.new(self) - gzip.mtime = @mtime - @body.each { |part| gzip << part } - @body.close if @body.respond_to?(:close) - gzip.close - @writer = nil - end - - def write(data) - @writer.call(data) - end - end - - class DeflateStream - DEFLATE_ARGS = [ - Zlib::DEFAULT_COMPRESSION, - # drop the zlib header which causes both Safari and IE to choke - -Zlib::MAX_WBITS, - Zlib::DEF_MEM_LEVEL, - Zlib::DEFAULT_STRATEGY - ] - - def initialize(body) - @body = body - end - - def each - deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS) - @body.each { |part| yield deflater.deflate(part) } - @body.close if @body.respond_to?(:close) - yield deflater.finish - nil - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/directory.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/directory.rb deleted file mode 100644 index acdd302..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/directory.rb +++ /dev/null @@ -1,153 +0,0 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' - -module Rack - # Rack::Directory serves entries below the +root+ given, according to the - # path info of the Rack request. If a directory is found, the file's contents - # will be presented in an html based index. If a file is found, the env will - # be passed to the specified +app+. - # - # If +app+ is not specified, a Rack::File of the same +root+ will be used. - - class Directory - DIR_FILE = "%s%s%s%s" - DIR_PAGE = <<-PAGE - - %s - - - -

%s

-
- - - - - - - -%s -
NameSizeTypeLast Modified
-
- - PAGE - - attr_reader :files - attr_accessor :root, :path - - def initialize(root, app=nil) - @root = F.expand_path(root) - @app = app || Rack::File.new(@root) - end - - def call(env) - dup._call(env) - end - - F = ::File - - def _call(env) - @env = env - @script_name = env['SCRIPT_NAME'] - @path_info = Utils.unescape(env['PATH_INFO']) - - if forbidden = check_forbidden - forbidden - else - @path = F.join(@root, @path_info) - list_path - end - end - - def check_forbidden - return unless @path_info.include? ".." - - body = "Forbidden\n" - size = Rack::Utils.bytesize(body) - return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]] - end - - def list_directory - @files = [['../','Parent Directory','','','']] - glob = F.join(@path, '*') - - Dir[glob].sort.each do |node| - stat = stat(node) - next unless stat - basename = F.basename(node) - ext = F.extname(node) - - url = F.join(@script_name, @path_info, basename) - size = stat.size - type = stat.directory? ? 'directory' : Mime.mime_type(ext) - size = stat.directory? ? '-' : filesize_format(size) - mtime = stat.mtime.httpdate - url << '/' if stat.directory? - basename << '/' if stat.directory? - - @files << [ url, basename, size, type, mtime ] - end - - return [ 200, {'Content-Type'=>'text/html; charset=utf-8'}, self ] - end - - def stat(node, max = 10) - F.stat(node) - rescue Errno::ENOENT, Errno::ELOOP - return nil - end - - # TODO: add correct response if not readable, not sure if 404 is the best - # option - def list_path - @stat = F.stat(@path) - - if @stat.readable? - return @app.call(@env) if @stat.file? - return list_directory if @stat.directory? - else - raise Errno::ENOENT, 'No such file or directory' - end - - rescue Errno::ENOENT, Errno::ELOOP - return entity_not_found - end - - def entity_not_found - body = "Entity not found: #{@path_info}\n" - size = Rack::Utils.bytesize(body) - return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]] - end - - def each - show_path = @path.sub(/^#{@root}/,'') - files = @files.map{|f| DIR_FILE % f }*"\n" - page = DIR_PAGE % [ show_path, show_path , files ] - page.each_line{|l| yield l } - end - - # Stolen from Ramaze - - FILESIZE_FORMAT = [ - ['%.1fT', 1 << 40], - ['%.1fG', 1 << 30], - ['%.1fM', 1 << 20], - ['%.1fK', 1 << 10], - ] - - def filesize_format(int) - FILESIZE_FORMAT.each do |format, size| - return format % (int.to_f / size) if int >= size - end - - int.to_s + 'B' - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/file.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/file.rb deleted file mode 100644 index fe62bd6..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/file.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'time' -require 'rack/utils' -require 'rack/mime' - -module Rack - # Rack::File serves files below the +root+ given, according to the - # path info of the Rack request. - # - # Handlers can detect if bodies are a Rack::File, and use mechanisms - # like sendfile on the +path+. - - class File - attr_accessor :root - attr_accessor :path - - alias :to_path :path - - def initialize(root) - @root = root - end - - def call(env) - dup._call(env) - end - - F = ::File - - def _call(env) - @path_info = Utils.unescape(env["PATH_INFO"]) - return forbidden if @path_info.include? ".." - - @path = F.join(@root, @path_info) - - begin - if F.file?(@path) && F.readable?(@path) - serving - else - raise Errno::EPERM - end - rescue SystemCallError - not_found - end - end - - def forbidden - body = "Forbidden\n" - [403, {"Content-Type" => "text/plain", - "Content-Length" => body.size.to_s}, - [body]] - end - - # NOTE: - # We check via File::size? whether this file provides size info - # via stat (e.g. /proc files often don't), otherwise we have to - # figure it out by reading the whole file into memory. And while - # we're at it we also use this as body then. - - def serving - if size = F.size?(@path) - body = self - else - body = [F.read(@path)] - size = Utils.bytesize(body.first) - end - - [200, { - "Last-Modified" => F.mtime(@path).httpdate, - "Content-Type" => Mime.mime_type(F.extname(@path), 'text/plain'), - "Content-Length" => size.to_s - }, body] - end - - def not_found - body = "File not found: #{@path_info}\n" - [404, {"Content-Type" => "text/plain", - "Content-Length" => body.size.to_s}, - [body]] - end - - def each - F.open(@path, "rb") { |file| - while part = file.read(8192) - yield part - end - } - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler.rb deleted file mode 100644 index 5624a1e..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler.rb +++ /dev/null @@ -1,69 +0,0 @@ -module Rack - # *Handlers* connect web servers with Rack. - # - # Rack includes Handlers for Mongrel, WEBrick, FastCGI, CGI, SCGI - # and LiteSpeed. - # - # Handlers usually are activated by calling MyHandler.run(myapp). - # A second optional hash can be passed to include server-specific - # configuration. - module Handler - def self.get(server) - return unless server - server = server.to_s - - if klass = @handlers[server] - obj = Object - klass.split("::").each { |x| obj = obj.const_get(x) } - obj - else - try_require('rack/handler', server) - const_get(server) - end - end - - # Transforms server-name constants to their canonical form as filenames, - # then tries to require them but silences the LoadError if not found - # - # Naming convention: - # - # Foo # => 'foo' - # FooBar # => 'foo_bar.rb' - # FooBAR # => 'foobar.rb' - # FOObar # => 'foobar.rb' - # FOOBAR # => 'foobar.rb' - # FooBarBaz # => 'foo_bar_baz.rb' - def self.try_require(prefix, const_name) - file = const_name.gsub(/^[A-Z]+/) { |pre| pre.downcase }. - gsub(/[A-Z]+[^A-Z]/, '_\&').downcase - - require(::File.join(prefix, file)) - rescue LoadError - end - - def self.register(server, klass) - @handlers ||= {} - @handlers[server] = klass - end - - autoload :CGI, "rack/handler/cgi" - autoload :FastCGI, "rack/handler/fastcgi" - autoload :Mongrel, "rack/handler/mongrel" - autoload :EventedMongrel, "rack/handler/evented_mongrel" - autoload :SwiftipliedMongrel, "rack/handler/swiftiplied_mongrel" - autoload :WEBrick, "rack/handler/webrick" - autoload :LSWS, "rack/handler/lsws" - autoload :SCGI, "rack/handler/scgi" - autoload :Thin, "rack/handler/thin" - - register 'cgi', 'Rack::Handler::CGI' - register 'fastcgi', 'Rack::Handler::FastCGI' - register 'mongrel', 'Rack::Handler::Mongrel' - register 'emongrel', 'Rack::Handler::EventedMongrel' - register 'smongrel', 'Rack::Handler::SwiftipliedMongrel' - register 'webrick', 'Rack::Handler::WEBrick' - register 'lsws', 'Rack::Handler::LSWS' - register 'scgi', 'Rack::Handler::SCGI' - register 'thin', 'Rack::Handler::Thin' - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/cgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/cgi.rb deleted file mode 100644 index f45f3d7..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/cgi.rb +++ /dev/null @@ -1,61 +0,0 @@ -require 'rack/content_length' - -module Rack - module Handler - class CGI - def self.run(app, options=nil) - serve app - end - - def self.serve(app) - app = ContentLength.new(app) - - env = ENV.to_hash - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - env.update({"rack.version" => [1,0], - "rack.input" => $stdin, - "rack.errors" => $stderr, - - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => true, - - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - - status, headers, body = app.call(env) - begin - send_headers status, headers - send_body body - ensure - body.close if body.respond_to? :close - end - end - - def self.send_headers(status, headers) - STDOUT.print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - STDOUT.print "#{k}: #{v}\r\n" - } - } - STDOUT.print "\r\n" - STDOUT.flush - end - - def self.send_body(body) - body.each { |part| - STDOUT.print part - STDOUT.flush - } - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/evented_mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/evented_mongrel.rb deleted file mode 100644 index 0f5cbf7..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/evented_mongrel.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'swiftcore/evented_mongrel' - -module Rack - module Handler - class EventedMongrel < Handler::Mongrel - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/fastcgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/fastcgi.rb deleted file mode 100644 index 11e1fca..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/fastcgi.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'fcgi' -require 'socket' -require 'rack/content_length' -require 'rack/rewindable_input' - -class FCGI::Stream - alias _rack_read_without_buffer read - - def read(n, buffer=nil) - buf = _rack_read_without_buffer n - buffer.replace(buf.to_s) if buffer - buf - end -end - -module Rack - module Handler - class FastCGI - def self.run(app, options={}) - file = options[:File] and STDIN.reopen(UNIXServer.new(file)) - port = options[:Port] and STDIN.reopen(TCPServer.new(port)) - FCGI.each { |request| - serve request, app - } - end - - def self.serve(request, app) - app = Rack::ContentLength.new(app) - - env = request.env - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - rack_input = RewindableInput.new(request.in) - - env.update({"rack.version" => [1,0], - "rack.input" => rack_input, - "rack.errors" => request.err, - - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" - }) - - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - env.delete "PATH_INFO" if env["PATH_INFO"] == "" - env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == "" - env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == "" - - begin - status, headers, body = app.call(env) - begin - send_headers request.out, status, headers - send_body request.out, body - ensure - body.close if body.respond_to? :close - end - ensure - rack_input.close - request.finish - end - end - - def self.send_headers(out, status, headers) - out.print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - out.print "#{k}: #{v}\r\n" - } - } - out.print "\r\n" - out.flush - end - - def self.send_body(out, body) - body.each { |part| - out.print part - out.flush - } - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/lsws.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/lsws.rb deleted file mode 100644 index 7231336..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/lsws.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'lsapi' -require 'rack/content_length' - -module Rack - module Handler - class LSWS - def self.run(app, options=nil) - while LSAPI.accept != nil - serve app - end - end - def self.serve(app) - app = Rack::ContentLength.new(app) - - env = ENV.to_hash - env.delete "HTTP_CONTENT_LENGTH" - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - env.update({"rack.version" => [1,0], - "rack.input" => StringIO.new($stdin.read.to_s), - "rack.errors" => $stderr, - "rack.multithread" => false, - "rack.multiprocess" => true, - "rack.run_once" => false, - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - env["QUERY_STRING"] ||= "" - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["REQUEST_PATH"] ||= "/" - status, headers, body = app.call(env) - begin - send_headers status, headers - send_body body - ensure - body.close if body.respond_to? :close - end - end - def self.send_headers(status, headers) - print "Status: #{status}\r\n" - headers.each { |k, vs| - vs.split("\n").each { |v| - print "#{k}: #{v}\r\n" - } - } - print "\r\n" - STDOUT.flush - end - def self.send_body(body) - body.each { |part| - print part - STDOUT.flush - } - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/mongrel.rb deleted file mode 100644 index 3a5ef32..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/mongrel.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'mongrel' -require 'stringio' -require 'rack/content_length' -require 'rack/chunked' - -module Rack - module Handler - class Mongrel < ::Mongrel::HttpHandler - def self.run(app, options={}) - server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0', - options[:Port] || 8080) - # Acts like Rack::URLMap, utilizing Mongrel's own path finding methods. - # Use is similar to #run, replacing the app argument with a hash of - # { path=>app, ... } or an instance of Rack::URLMap. - if options[:map] - if app.is_a? Hash - app.each do |path, appl| - path = '/'+path unless path[0] == ?/ - server.register(path, Rack::Handler::Mongrel.new(appl)) - end - elsif app.is_a? URLMap - app.instance_variable_get(:@mapping).each do |(host, path, appl)| - next if !host.nil? && !options[:Host].nil? && options[:Host] != host - path = '/'+path unless path[0] == ?/ - server.register(path, Rack::Handler::Mongrel.new(appl)) - end - else - raise ArgumentError, "first argument should be a Hash or URLMap" - end - else - server.register('/', Rack::Handler::Mongrel.new(app)) - end - yield server if block_given? - server.run.join - end - - def initialize(app) - @app = Rack::Chunked.new(Rack::ContentLength.new(app)) - end - - def process(request, response) - env = {}.replace(request.params) - env.delete "HTTP_CONTENT_TYPE" - env.delete "HTTP_CONTENT_LENGTH" - - env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - - env.update({"rack.version" => [1,0], - "rack.input" => request.body || StringIO.new(""), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => false, # ??? - "rack.run_once" => false, - - "rack.url_scheme" => "http", - }) - env["QUERY_STRING"] ||= "" - env.delete "PATH_INFO" if env["PATH_INFO"] == "" - - status, headers, body = @app.call(env) - - begin - response.status = status.to_i - response.send_status(nil) - - headers.each { |k, vs| - vs.split("\n").each { |v| - response.header[k] = v - } - } - response.send_header - - body.each { |part| - response.write part - response.socket.flush - } - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/scgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/scgi.rb deleted file mode 100644 index 6c4932d..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/scgi.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'scgi' -require 'stringio' -require 'rack/content_length' -require 'rack/chunked' - -module Rack - module Handler - class SCGI < ::SCGI::Processor - attr_accessor :app - - def self.run(app, options=nil) - new(options.merge(:app=>app, - :host=>options[:Host], - :port=>options[:Port], - :socket=>options[:Socket])).listen - end - - def initialize(settings = {}) - @app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app])) - @log = Object.new - def @log.info(*args); end - def @log.error(*args); end - super(settings) - end - - def process_request(request, input_body, socket) - env = {}.replace(request) - env.delete "HTTP_CONTENT_TYPE" - env.delete "HTTP_CONTENT_LENGTH" - env["REQUEST_PATH"], env["QUERY_STRING"] = env["REQUEST_URI"].split('?', 2) - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["PATH_INFO"] = env["REQUEST_PATH"] - env["QUERY_STRING"] ||= "" - env["SCRIPT_NAME"] = "" - env.update({"rack.version" => [1,0], - "rack.input" => StringIO.new(input_body), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => true, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" - }) - status, headers, body = app.call(env) - begin - socket.write("Status: #{status}\r\n") - headers.each do |k, vs| - vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")} - end - socket.write("\r\n") - body.each {|s| socket.write(s)} - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/swiftiplied_mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/swiftiplied_mongrel.rb deleted file mode 100644 index 4bafd0b..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/swiftiplied_mongrel.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'swiftcore/swiftiplied_mongrel' - -module Rack - module Handler - class SwiftipliedMongrel < Handler::Mongrel - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/thin.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/thin.rb deleted file mode 100644 index 3d4fedf..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/thin.rb +++ /dev/null @@ -1,18 +0,0 @@ -require "thin" -require "rack/content_length" -require "rack/chunked" - -module Rack - module Handler - class Thin - def self.run(app, options={}) - app = Rack::Chunked.new(Rack::ContentLength.new(app)) - server = ::Thin::Server.new(options[:Host] || '0.0.0.0', - options[:Port] || 8080, - app) - yield server if block_given? - server.start - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/webrick.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/webrick.rb deleted file mode 100644 index a8b6ff5..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/handler/webrick.rb +++ /dev/null @@ -1,68 +0,0 @@ -require 'webrick' -require 'stringio' -require 'rack/content_length' - -module Rack - module Handler - class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet - def self.run(app, options={}) - options[:BindAddress] = options.delete(:Host) if options[:Host] - server = ::WEBrick::HTTPServer.new(options) - server.mount "/", Rack::Handler::WEBrick, app - trap(:INT) { server.shutdown } - yield server if block_given? - server.start - end - - def initialize(server, app) - super server - @app = Rack::ContentLength.new(app) - end - - def service(req, res) - env = req.meta_vars - env.delete_if { |k, v| v.nil? } - - env.update({"rack.version" => [1,0], - "rack.input" => StringIO.new(req.body.to_s), - "rack.errors" => $stderr, - - "rack.multithread" => true, - "rack.multiprocess" => false, - "rack.run_once" => false, - - "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http" - }) - - env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] - env["QUERY_STRING"] ||= "" - env["REQUEST_PATH"] ||= "/" - if env["PATH_INFO"] == "" - env.delete "PATH_INFO" - else - path, n = req.request_uri.path, env["SCRIPT_NAME"].length - env["PATH_INFO"] = path[n, path.length-n] - end - - status, headers, body = @app.call(env) - begin - res.status = status.to_i - headers.each { |k, vs| - if k.downcase == "set-cookie" - res.cookies.concat vs.split("\n") - else - vs.split("\n").each { |v| - res[k] = v - } - end - } - body.each { |part| - res.body << part - } - ensure - body.close if body.respond_to? :close - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/head.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/head.rb deleted file mode 100644 index deab822..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/head.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Rack - -class Head - def initialize(app) - @app = app - end - - def call(env) - status, headers, body = @app.call(env) - - if env["REQUEST_METHOD"] == "HEAD" - [status, headers, []] - else - [status, headers, body] - end - end -end - -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lint.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lint.rb deleted file mode 100644 index bf2e978..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lint.rb +++ /dev/null @@ -1,537 +0,0 @@ -require 'rack/utils' - -module Rack - # Rack::Lint validates your application and the requests and - # responses according to the Rack spec. - - class Lint - def initialize(app) - @app = app - end - - # :stopdoc: - - class LintError < RuntimeError; end - module Assertion - def assert(message, &block) - unless block.call - raise LintError, message - end - end - end - include Assertion - - ## This specification aims to formalize the Rack protocol. You - ## can (and should) use Rack::Lint to enforce it. - ## - ## When you develop middleware, be sure to add a Lint before and - ## after to catch all mistakes. - - ## = Rack applications - - ## A Rack application is an Ruby object (not a class) that - ## responds to +call+. - def call(env=nil) - dup._call(env) - end - - def _call(env) - ## It takes exactly one argument, the *environment* - assert("No env given") { env } - check_env env - - env['rack.input'] = InputWrapper.new(env['rack.input']) - env['rack.errors'] = ErrorWrapper.new(env['rack.errors']) - - ## and returns an Array of exactly three values: - status, headers, @body = @app.call(env) - ## The *status*, - check_status status - ## the *headers*, - check_headers headers - ## and the *body*. - check_content_type status, headers - check_content_length status, headers, env - [status, headers, self] - end - - ## == The Environment - def check_env(env) - ## The environment must be an true instance of Hash (no - ## subclassing allowed) that includes CGI-like headers. - ## The application is free to modify the environment. - assert("env #{env.inspect} is not a Hash, but #{env.class}") { - env.instance_of? Hash - } - - ## - ## The environment is required to include these variables - ## (adopted from PEP333), except when they'd be empty, but see - ## below. - - ## REQUEST_METHOD:: The HTTP request method, such as - ## "GET" or "POST". This cannot ever - ## be an empty string, and so is - ## always required. - - ## SCRIPT_NAME:: The initial portion of the request - ## URL's "path" that corresponds to the - ## application object, so that the - ## application knows its virtual - ## "location". This may be an empty - ## string, if the application corresponds - ## to the "root" of the server. - - ## PATH_INFO:: The remainder of the request URL's - ## "path", designating the virtual - ## "location" of the request's target - ## within the application. This may be an - ## empty string, if the request URL targets - ## the application root and does not have a - ## trailing slash. This value may be - ## percent-encoded when I originating from - ## a URL. - - ## QUERY_STRING:: The portion of the request URL that - ## follows the ?, if any. May be - ## empty, but is always required! - - ## SERVER_NAME, SERVER_PORT:: When combined with SCRIPT_NAME and PATH_INFO, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to SERVER_NAME for reconstructing the request URL. SERVER_NAME and SERVER_PORT can never be empty strings, and so are always required. - - ## HTTP_ Variables:: Variables corresponding to the - ## client-supplied HTTP request - ## headers (i.e., variables whose - ## names begin with HTTP_). The - ## presence or absence of these - ## variables should correspond with - ## the presence or absence of the - ## appropriate HTTP header in the - ## request. - - ## In addition to this, the Rack environment must include these - ## Rack-specific variables: - - ## rack.version:: The Array [1,0], representing this version of Rack. - ## rack.url_scheme:: +http+ or +https+, depending on the request URL. - ## rack.input:: See below, the input stream. - ## rack.errors:: See below, the error stream. - ## rack.multithread:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise. - ## rack.multiprocess:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise. - ## rack.run_once:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar). - ## - - ## Additional environment specifications have approved to - ## standardized middleware APIs. None of these are required to - ## be implemented by the server. - - ## rack.session:: A hash like interface for storing request session data. - ## The store must implement: - if session = env['rack.session'] - ## store(key, value) (aliased as []=); - assert("session #{session.inspect} must respond to store and []=") { - session.respond_to?(:store) && session.respond_to?(:[]=) - } - - ## fetch(key, default = nil) (aliased as []); - assert("session #{session.inspect} must respond to fetch and []") { - session.respond_to?(:fetch) && session.respond_to?(:[]) - } - - ## delete(key); - assert("session #{session.inspect} must respond to delete") { - session.respond_to?(:delete) - } - - ## clear; - assert("session #{session.inspect} must respond to clear") { - session.respond_to?(:clear) - } - end - - ## The server or the application can store their own data in the - ## environment, too. The keys must contain at least one dot, - ## and should be prefixed uniquely. The prefix rack. - ## is reserved for use with the Rack core distribution and other - ## accepted specifications and must not be used otherwise. - ## - - %w[REQUEST_METHOD SERVER_NAME SERVER_PORT - QUERY_STRING - rack.version rack.input rack.errors - rack.multithread rack.multiprocess rack.run_once].each { |header| - assert("env missing required key #{header}") { env.include? header } - } - - ## The environment must not contain the keys - ## HTTP_CONTENT_TYPE or HTTP_CONTENT_LENGTH - ## (use the versions without HTTP_). - %w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header| - assert("env contains #{header}, must use #{header[5,-1]}") { - not env.include? header - } - } - - ## The CGI keys (named without a period) must have String values. - env.each { |key, value| - next if key.include? "." # Skip extensions - assert("env variable #{key} has non-string value #{value.inspect}") { - value.instance_of? String - } - } - - ## - ## There are the following restrictions: - - ## * rack.version must be an array of Integers. - assert("rack.version must be an Array, was #{env["rack.version"].class}") { - env["rack.version"].instance_of? Array - } - ## * rack.url_scheme must either be +http+ or +https+. - assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") { - %w[http https].include? env["rack.url_scheme"] - } - - ## * There must be a valid input stream in rack.input. - check_input env["rack.input"] - ## * There must be a valid error stream in rack.errors. - check_error env["rack.errors"] - - ## * The REQUEST_METHOD must be a valid token. - assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") { - env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/ - } - - ## * The SCRIPT_NAME, if non-empty, must start with / - assert("SCRIPT_NAME must start with /") { - !env.include?("SCRIPT_NAME") || - env["SCRIPT_NAME"] == "" || - env["SCRIPT_NAME"] =~ /\A\// - } - ## * The PATH_INFO, if non-empty, must start with / - assert("PATH_INFO must start with /") { - !env.include?("PATH_INFO") || - env["PATH_INFO"] == "" || - env["PATH_INFO"] =~ /\A\// - } - ## * The CONTENT_LENGTH, if given, must consist of digits only. - assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") { - !env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/ - } - - ## * One of SCRIPT_NAME or PATH_INFO must be - ## set. PATH_INFO should be / if - ## SCRIPT_NAME is empty. - assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") { - env["SCRIPT_NAME"] || env["PATH_INFO"] - } - ## SCRIPT_NAME never should be /, but instead be empty. - assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") { - env["SCRIPT_NAME"] != "/" - } - end - - ## === The Input Stream - ## - ## The input stream is an IO-like object which contains the raw HTTP - ## POST data. If it is a file then it must be opened in binary mode. - def check_input(input) - ## The input stream must respond to +gets+, +each+, +read+ and +rewind+. - [:gets, :each, :read, :rewind].each { |method| - assert("rack.input #{input} does not respond to ##{method}") { - input.respond_to? method - } - } - end - - class InputWrapper - include Assertion - - def initialize(input) - @input = input - end - - def size - @input.size - end - - ## * +gets+ must be called without arguments and return a string, - ## or +nil+ on EOF. - def gets(*args) - assert("rack.input#gets called with arguments") { args.size == 0 } - v = @input.gets - assert("rack.input#gets didn't return a String") { - v.nil? or v.instance_of? String - } - v - end - - ## * +read+ behaves like IO#read. Its signature is read([length, [buffer]]). - ## If given, +length+ must be an non-negative Integer (>= 0) or +nil+, and +buffer+ must - ## be a String and may not be nil. If +length+ is given and not nil, then this method - ## reads at most +length+ bytes from the input stream. If +length+ is not given or nil, - ## then this method reads all data until EOF. - ## When EOF is reached, this method returns nil if +length+ is given and not nil, or "" - ## if +length+ is not given or is nil. - ## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a - ## newly created String object. - def read(*args) - assert("rack.input#read called with too many arguments") { - args.size <= 2 - } - if args.size >= 1 - assert("rack.input#read called with non-integer and non-nil length") { - args.first.kind_of?(Integer) || args.first.nil? - } - assert("rack.input#read called with a negative length") { - args.first.nil? || args.first >= 0 - } - end - if args.size >= 2 - assert("rack.input#read called with non-String buffer") { - args[1].kind_of?(String) - } - end - - v = @input.read(*args) - - assert("rack.input#read didn't return nil or a String") { - v.nil? or v.instance_of? String - } - if args[0].nil? - assert("rack.input#read(nil) returned nil on EOF") { - !v.nil? - } - end - - v - end - - ## * +each+ must be called without arguments and only yield Strings. - def each(*args) - assert("rack.input#each called with arguments") { args.size == 0 } - @input.each { |line| - assert("rack.input#each didn't yield a String") { - line.instance_of? String - } - yield line - } - end - - ## * +rewind+ must be called without arguments. It rewinds the input - ## stream back to the beginning. It must not raise Errno::ESPIPE: - ## that is, it may not be a pipe or a socket. Therefore, handler - ## developers must buffer the input data into some rewindable object - ## if the underlying input stream is not rewindable. - def rewind(*args) - assert("rack.input#rewind called with arguments") { args.size == 0 } - assert("rack.input#rewind raised Errno::ESPIPE") { - begin - @input.rewind - true - rescue Errno::ESPIPE - false - end - } - end - - ## * +close+ must never be called on the input stream. - def close(*args) - assert("rack.input#close must not be called") { false } - end - end - - ## === The Error Stream - def check_error(error) - ## The error stream must respond to +puts+, +write+ and +flush+. - [:puts, :write, :flush].each { |method| - assert("rack.error #{error} does not respond to ##{method}") { - error.respond_to? method - } - } - end - - class ErrorWrapper - include Assertion - - def initialize(error) - @error = error - end - - ## * +puts+ must be called with a single argument that responds to +to_s+. - def puts(str) - @error.puts str - end - - ## * +write+ must be called with a single argument that is a String. - def write(str) - assert("rack.errors#write not called with a String") { str.instance_of? String } - @error.write str - end - - ## * +flush+ must be called without arguments and must be called - ## in order to make the error appear for sure. - def flush - @error.flush - end - - ## * +close+ must never be called on the error stream. - def close(*args) - assert("rack.errors#close must not be called") { false } - end - end - - ## == The Response - - ## === The Status - def check_status(status) - ## This is an HTTP status. When parsed as integer (+to_i+), it must be - ## greater than or equal to 100. - assert("Status must be >=100 seen as integer") { status.to_i >= 100 } - end - - ## === The Headers - def check_headers(header) - ## The header must respond to +each+, and yield values of key and value. - assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") { - header.respond_to? :each - } - header.each { |key, value| - ## The header keys must be Strings. - assert("header key must be a string, was #{key.class}") { - key.instance_of? String - } - ## The header must not contain a +Status+ key, - assert("header must not contain Status") { key.downcase != "status" } - ## contain keys with : or newlines in their name, - assert("header names must not contain : or \\n") { key !~ /[:\n]/ } - ## contain keys names that end in - or _, - assert("header names must not end in - or _") { key !~ /[-_]\z/ } - ## but only contain keys that consist of - ## letters, digits, _ or - and start with a letter. - assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ } - - ## The values of the header must be Strings, - assert("a header value must be a String, but the value of " + - "'#{key}' is a #{value.class}") { value.kind_of? String } - ## consisting of lines (for multiple header values, e.g. multiple - ## Set-Cookie values) seperated by "\n". - value.split("\n").each { |item| - ## The lines must not contain characters below 037. - assert("invalid header value #{key}: #{item.inspect}") { - item !~ /[\000-\037]/ - } - } - } - end - - ## === The Content-Type - def check_content_type(status, headers) - headers.each { |key, value| - ## There must be a Content-Type, except when the - ## +Status+ is 1xx, 204 or 304, in which case there must be none - ## given. - if key.downcase == "content-type" - assert("Content-Type header found in #{status} response, not allowed") { - not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - return - end - } - assert("No Content-Type header found") { - Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - end - - ## === The Content-Length - def check_content_length(status, headers, env) - headers.each { |key, value| - if key.downcase == 'content-length' - ## There must not be a Content-Length header when the - ## +Status+ is 1xx, 204 or 304. - assert("Content-Length header found in #{status} response, not allowed") { - not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - - bytes = 0 - string_body = true - - if @body.respond_to?(:to_ary) - @body.each { |part| - unless part.kind_of?(String) - string_body = false - break - end - - bytes += Rack::Utils.bytesize(part) - } - - if env["REQUEST_METHOD"] == "HEAD" - assert("Response body was given for HEAD request, but should be empty") { - bytes == 0 - } - else - if string_body - assert("Content-Length header was #{value}, but should be #{bytes}") { - value == bytes.to_s - } - end - end - end - - return - end - } - end - - ## === The Body - def each - @closed = false - ## The Body must respond to +each+ - @body.each { |part| - ## and must only yield String values. - assert("Body yielded non-string value #{part.inspect}") { - part.instance_of? String - } - yield part - } - ## - ## The Body itself should not be an instance of String, as this will - ## break in Ruby 1.9. - ## - ## If the Body responds to +close+, it will be called after iteration. - # XXX howto: assert("Body has not been closed") { @closed } - - - ## - ## If the Body responds to +to_path+, it must return a String - ## identifying the location of a file whose contents are identical - ## to that produced by calling +each+; this may be used by the - ## server as an alternative, possibly more efficient way to - ## transport the response. - - if @body.respond_to?(:to_path) - assert("The file identified by body.to_path does not exist") { - ::File.exist? @body.to_path - } - end - - ## - ## The Body commonly is an Array of Strings, the application - ## instance itself, or a File-like object. - end - - def close - @closed = true - @body.close if @body.respond_to?(:close) - end - - # :startdoc: - - end -end - -## == Thanks -## Some parts of this specification are adopted from PEP333: Python -## Web Server Gateway Interface -## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank -## everyone involved in that effort. diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lobster.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lobster.rb deleted file mode 100644 index f63f419..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lobster.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'zlib' - -require 'rack/request' -require 'rack/response' - -module Rack - # Paste has a Pony, Rack has a Lobster! - class Lobster - LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2 - P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0 - t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ - I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0]) - - LambdaLobster = lambda { |env| - if env["QUERY_STRING"].include?("flip") - lobster = LobsterString.split("\n"). - map { |line| line.ljust(42).reverse }. - join("\n") - href = "?" - else - lobster = LobsterString - href = "?flip" - end - - content = ["Lobstericious!", - "
", lobster, "
", - "flip!"] - length = content.inject(0) { |a,e| a+e.size }.to_s - [200, {"Content-Type" => "text/html", "Content-Length" => length}, content] - } - - def call(env) - req = Request.new(env) - if req.GET["flip"] == "left" - lobster = LobsterString.split("\n"). - map { |line| line.ljust(42).reverse }. - join("\n") - href = "?flip=right" - elsif req.GET["flip"] == "crash" - raise "Lobster crashed" - else - lobster = LobsterString - href = "?flip=left" - end - - res = Response.new - res.write "Lobstericious!" - res.write "
"
-      res.write lobster
-      res.write "
" - res.write "

flip!

" - res.write "

crash!

" - res.finish - end - - end -end - -if $0 == __FILE__ - require 'rack' - require 'rack/showexceptions' - Rack::Handler::WEBrick.run \ - Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), - :Port => 9292 -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lock.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lock.rb deleted file mode 100644 index 9323852..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/lock.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Rack - class Lock - FLAG = 'rack.multithread'.freeze - - def initialize(app, lock = Mutex.new) - @app, @lock = app, lock - end - - def call(env) - old, env[FLAG] = env[FLAG], false - @lock.synchronize { @app.call(env) } - ensure - env[FLAG] = old - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/methodoverride.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/methodoverride.rb deleted file mode 100644 index 0eed29f..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/methodoverride.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Rack - class MethodOverride - HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS) - - METHOD_OVERRIDE_PARAM_KEY = "_method".freeze - HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze - - def initialize(app) - @app = app - end - - def call(env) - if env["REQUEST_METHOD"] == "POST" - req = Request.new(env) - method = req.POST[METHOD_OVERRIDE_PARAM_KEY] || - env[HTTP_METHOD_OVERRIDE_HEADER] - method = method.to_s.upcase - if HTTP_METHODS.include?(method) - env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"] - env["REQUEST_METHOD"] = method - end - end - - @app.call(env) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mime.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mime.rb deleted file mode 100644 index 5a6a73a..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mime.rb +++ /dev/null @@ -1,204 +0,0 @@ -module Rack - module Mime - # Returns String with mime type if found, otherwise use +fallback+. - # +ext+ should be filename extension in the '.ext' format that - # File.extname(file) returns. - # +fallback+ may be any object - # - # Also see the documentation for MIME_TYPES - # - # Usage: - # Rack::Mime.mime_type('.foo') - # - # This is a shortcut for: - # Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream') - - def mime_type(ext, fallback='application/octet-stream') - MIME_TYPES.fetch(ext, fallback) - end - module_function :mime_type - - # List of most common mime-types, selected various sources - # according to their usefulness in a webserving scope for Ruby - # users. - # - # To amend this list with your local mime.types list you can use: - # - # require 'webrick/httputils' - # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types') - # Rack::Mime::MIME_TYPES.merge!(list) - # - # To add the list mongrel provides, use: - # - # require 'mongrel/handlers' - # Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES) - - MIME_TYPES = { - ".3gp" => "video/3gpp", - ".a" => "application/octet-stream", - ".ai" => "application/postscript", - ".aif" => "audio/x-aiff", - ".aiff" => "audio/x-aiff", - ".asc" => "application/pgp-signature", - ".asf" => "video/x-ms-asf", - ".asm" => "text/x-asm", - ".asx" => "video/x-ms-asf", - ".atom" => "application/atom+xml", - ".au" => "audio/basic", - ".avi" => "video/x-msvideo", - ".bat" => "application/x-msdownload", - ".bin" => "application/octet-stream", - ".bmp" => "image/bmp", - ".bz2" => "application/x-bzip2", - ".c" => "text/x-c", - ".cab" => "application/vnd.ms-cab-compressed", - ".cc" => "text/x-c", - ".chm" => "application/vnd.ms-htmlhelp", - ".class" => "application/octet-stream", - ".com" => "application/x-msdownload", - ".conf" => "text/plain", - ".cpp" => "text/x-c", - ".crt" => "application/x-x509-ca-cert", - ".css" => "text/css", - ".csv" => "text/csv", - ".cxx" => "text/x-c", - ".deb" => "application/x-debian-package", - ".der" => "application/x-x509-ca-cert", - ".diff" => "text/x-diff", - ".djv" => "image/vnd.djvu", - ".djvu" => "image/vnd.djvu", - ".dll" => "application/x-msdownload", - ".dmg" => "application/octet-stream", - ".doc" => "application/msword", - ".dot" => "application/msword", - ".dtd" => "application/xml-dtd", - ".dvi" => "application/x-dvi", - ".ear" => "application/java-archive", - ".eml" => "message/rfc822", - ".eps" => "application/postscript", - ".exe" => "application/x-msdownload", - ".f" => "text/x-fortran", - ".f77" => "text/x-fortran", - ".f90" => "text/x-fortran", - ".flv" => "video/x-flv", - ".for" => "text/x-fortran", - ".gem" => "application/octet-stream", - ".gemspec" => "text/x-script.ruby", - ".gif" => "image/gif", - ".gz" => "application/x-gzip", - ".h" => "text/x-c", - ".hh" => "text/x-c", - ".htm" => "text/html", - ".html" => "text/html", - ".ico" => "image/vnd.microsoft.icon", - ".ics" => "text/calendar", - ".ifb" => "text/calendar", - ".iso" => "application/octet-stream", - ".jar" => "application/java-archive", - ".java" => "text/x-java-source", - ".jnlp" => "application/x-java-jnlp-file", - ".jpeg" => "image/jpeg", - ".jpg" => "image/jpeg", - ".js" => "application/javascript", - ".json" => "application/json", - ".log" => "text/plain", - ".m3u" => "audio/x-mpegurl", - ".m4v" => "video/mp4", - ".man" => "text/troff", - ".mathml" => "application/mathml+xml", - ".mbox" => "application/mbox", - ".mdoc" => "text/troff", - ".me" => "text/troff", - ".mid" => "audio/midi", - ".midi" => "audio/midi", - ".mime" => "message/rfc822", - ".mml" => "application/mathml+xml", - ".mng" => "video/x-mng", - ".mov" => "video/quicktime", - ".mp3" => "audio/mpeg", - ".mp4" => "video/mp4", - ".mp4v" => "video/mp4", - ".mpeg" => "video/mpeg", - ".mpg" => "video/mpeg", - ".ms" => "text/troff", - ".msi" => "application/x-msdownload", - ".odp" => "application/vnd.oasis.opendocument.presentation", - ".ods" => "application/vnd.oasis.opendocument.spreadsheet", - ".odt" => "application/vnd.oasis.opendocument.text", - ".ogg" => "application/ogg", - ".p" => "text/x-pascal", - ".pas" => "text/x-pascal", - ".pbm" => "image/x-portable-bitmap", - ".pdf" => "application/pdf", - ".pem" => "application/x-x509-ca-cert", - ".pgm" => "image/x-portable-graymap", - ".pgp" => "application/pgp-encrypted", - ".pkg" => "application/octet-stream", - ".pl" => "text/x-script.perl", - ".pm" => "text/x-script.perl-module", - ".png" => "image/png", - ".pnm" => "image/x-portable-anymap", - ".ppm" => "image/x-portable-pixmap", - ".pps" => "application/vnd.ms-powerpoint", - ".ppt" => "application/vnd.ms-powerpoint", - ".ps" => "application/postscript", - ".psd" => "image/vnd.adobe.photoshop", - ".py" => "text/x-script.python", - ".qt" => "video/quicktime", - ".ra" => "audio/x-pn-realaudio", - ".rake" => "text/x-script.ruby", - ".ram" => "audio/x-pn-realaudio", - ".rar" => "application/x-rar-compressed", - ".rb" => "text/x-script.ruby", - ".rdf" => "application/rdf+xml", - ".roff" => "text/troff", - ".rpm" => "application/x-redhat-package-manager", - ".rss" => "application/rss+xml", - ".rtf" => "application/rtf", - ".ru" => "text/x-script.ruby", - ".s" => "text/x-asm", - ".sgm" => "text/sgml", - ".sgml" => "text/sgml", - ".sh" => "application/x-sh", - ".sig" => "application/pgp-signature", - ".snd" => "audio/basic", - ".so" => "application/octet-stream", - ".svg" => "image/svg+xml", - ".svgz" => "image/svg+xml", - ".swf" => "application/x-shockwave-flash", - ".t" => "text/troff", - ".tar" => "application/x-tar", - ".tbz" => "application/x-bzip-compressed-tar", - ".tcl" => "application/x-tcl", - ".tex" => "application/x-tex", - ".texi" => "application/x-texinfo", - ".texinfo" => "application/x-texinfo", - ".text" => "text/plain", - ".tif" => "image/tiff", - ".tiff" => "image/tiff", - ".torrent" => "application/x-bittorrent", - ".tr" => "text/troff", - ".txt" => "text/plain", - ".vcf" => "text/x-vcard", - ".vcs" => "text/x-vcalendar", - ".vrml" => "model/vrml", - ".war" => "application/java-archive", - ".wav" => "audio/x-wav", - ".wma" => "audio/x-ms-wma", - ".wmv" => "video/x-ms-wmv", - ".wmx" => "video/x-ms-wmx", - ".wrl" => "model/vrml", - ".wsdl" => "application/wsdl+xml", - ".xbm" => "image/x-xbitmap", - ".xhtml" => "application/xhtml+xml", - ".xls" => "application/vnd.ms-excel", - ".xml" => "application/xml", - ".xpm" => "image/x-xpixmap", - ".xsl" => "application/xml", - ".xslt" => "application/xslt+xml", - ".yaml" => "text/yaml", - ".yml" => "text/yaml", - ".zip" => "application/zip", - } - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mock.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mock.rb deleted file mode 100644 index fdefb03..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/mock.rb +++ /dev/null @@ -1,184 +0,0 @@ -require 'uri' -require 'stringio' -require 'rack/lint' -require 'rack/utils' -require 'rack/response' - -module Rack - # Rack::MockRequest helps testing your Rack application without - # actually using HTTP. - # - # After performing a request on a URL with get/post/put/delete, it - # returns a MockResponse with useful helper methods for effective - # testing. - # - # You can pass a hash with additional configuration to the - # get/post/put/delete. - # :input:: A String or IO-like to be used as rack.input. - # :fatal:: Raise a FatalWarning if the app writes to rack.errors. - # :lint:: If true, wrap the application in a Rack::Lint. - - class MockRequest - class FatalWarning < RuntimeError - end - - class FatalWarner - def puts(warning) - raise FatalWarning, warning - end - - def write(warning) - raise FatalWarning, warning - end - - def flush - end - - def string - "" - end - end - - DEFAULT_ENV = { - "rack.version" => [1,0], - "rack.input" => StringIO.new, - "rack.errors" => StringIO.new, - "rack.multithread" => true, - "rack.multiprocess" => true, - "rack.run_once" => false, - } - - def initialize(app) - @app = app - end - - def get(uri, opts={}) request("GET", uri, opts) end - def post(uri, opts={}) request("POST", uri, opts) end - def put(uri, opts={}) request("PUT", uri, opts) end - def delete(uri, opts={}) request("DELETE", uri, opts) end - - def request(method="GET", uri="", opts={}) - env = self.class.env_for(uri, opts.merge(:method => method)) - - if opts[:lint] - app = Rack::Lint.new(@app) - else - app = @app - end - - errors = env["rack.errors"] - MockResponse.new(*(app.call(env) + [errors])) - end - - # Return the Rack environment used for a request to +uri+. - def self.env_for(uri="", opts={}) - uri = URI(uri) - uri.path = "/#{uri.path}" unless uri.path[0] == ?/ - - env = DEFAULT_ENV.dup - - env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET" - env["SERVER_NAME"] = uri.host || "example.org" - env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80" - env["QUERY_STRING"] = uri.query.to_s - env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path - env["rack.url_scheme"] = uri.scheme || "http" - env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off" - - env["SCRIPT_NAME"] = opts[:script_name] || "" - - if opts[:fatal] - env["rack.errors"] = FatalWarner.new - else - env["rack.errors"] = StringIO.new - end - - if params = opts[:params] - if env["REQUEST_METHOD"] == "GET" - params = Utils.parse_nested_query(params) if params.is_a?(String) - params.update(Utils.parse_nested_query(env["QUERY_STRING"])) - env["QUERY_STRING"] = Utils.build_nested_query(params) - elsif !opts.has_key?(:input) - opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded" - if params.is_a?(Hash) - if data = Utils::Multipart.build_multipart(params) - opts[:input] = data - opts["CONTENT_LENGTH"] ||= data.length.to_s - opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}" - else - opts[:input] = Utils.build_nested_query(params) - end - else - opts[:input] = params - end - end - end - - opts[:input] ||= "" - if String === opts[:input] - env["rack.input"] = StringIO.new(opts[:input]) - else - env["rack.input"] = opts[:input] - end - - env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s - - opts.each { |field, value| - env[field] = value if String === field - } - - env - end - end - - # Rack::MockResponse provides useful helpers for testing your apps. - # Usually, you don't create the MockResponse on your own, but use - # MockRequest. - - class MockResponse - def initialize(status, headers, body, errors=StringIO.new("")) - @status = status.to_i - - @original_headers = headers - @headers = Rack::Utils::HeaderHash.new - headers.each { |field, values| - @headers[field] = values - @headers[field] = "" if values.empty? - } - - @body = "" - body.each { |part| @body << part } - - @errors = errors.string if errors.respond_to?(:string) - end - - # Status - attr_reader :status - - # Headers - attr_reader :headers, :original_headers - - def [](field) - headers[field] - end - - - # Body - attr_reader :body - - def =~(other) - @body =~ other - end - - def match(other) - @body.match other - end - - - # Errors - attr_accessor :errors - - - include Response::Helpers - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/recursive.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/recursive.rb deleted file mode 100644 index bf8b965..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/recursive.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'uri' - -module Rack - # Rack::ForwardRequest gets caught by Rack::Recursive and redirects - # the current request to the app at +url+. - # - # raise ForwardRequest.new("/not-found") - # - - class ForwardRequest < Exception - attr_reader :url, :env - - def initialize(url, env={}) - @url = URI(url) - @env = env - - @env["PATH_INFO"] = @url.path - @env["QUERY_STRING"] = @url.query if @url.query - @env["HTTP_HOST"] = @url.host if @url.host - @env["HTTP_PORT"] = @url.port if @url.port - @env["rack.url_scheme"] = @url.scheme if @url.scheme - - super "forwarding to #{url}" - end - end - - # Rack::Recursive allows applications called down the chain to - # include data from other applications (by using - # rack['rack.recursive.include'][...] or raise a - # ForwardRequest to redirect internally. - - class Recursive - def initialize(app) - @app = app - end - - def call(env) - @script_name = env["SCRIPT_NAME"] - @app.call(env.merge('rack.recursive.include' => method(:include))) - rescue ForwardRequest => req - call(env.merge(req.env)) - end - - def include(env, path) - unless path.index(@script_name) == 0 && (path[@script_name.size] == ?/ || - path[@script_name.size].nil?) - raise ArgumentError, "can only include below #{@script_name}, not #{path}" - end - - env = env.merge("PATH_INFO" => path, "SCRIPT_NAME" => @script_name, - "REQUEST_METHOD" => "GET", - "CONTENT_LENGTH" => "0", "CONTENT_TYPE" => "", - "rack.input" => StringIO.new("")) - @app.call(env) - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/reloader.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/reloader.rb deleted file mode 100644 index a9c566f..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/reloader.rb +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com -# All files in this distribution are subject to the terms of the Ruby license. - -require 'pathname' - -module Rack - - # High performant source reloader - # - # This class acts as Rack middleware. - # - # What makes it especially suited for use in a production environment is that - # any file will only be checked once and there will only be made one system - # call stat(2). - # - # Please note that this will not reload files in the background, it does so - # only when actively called. - # - # It is performing a check/reload cycle at the start of every request, but - # also respects a cool down time, during which nothing will be done. - class Reloader - def initialize(app, cooldown = 10, backend = Stat) - @app = app - @cooldown = cooldown - @last = (Time.now - cooldown) - @cache = {} - @mtimes = {} - - extend backend - end - - def call(env) - if @cooldown and Time.now > @last + @cooldown - if Thread.list.size > 1 - Thread.exclusive{ reload! } - else - reload! - end - - @last = Time.now - end - - @app.call(env) - end - - def reload!(stderr = $stderr) - rotation do |file, mtime| - previous_mtime = @mtimes[file] ||= mtime - safe_load(file, mtime, stderr) if mtime > previous_mtime - end - end - - # A safe Kernel::load, issuing the hooks depending on the results - def safe_load(file, mtime, stderr = $stderr) - load(file) - stderr.puts "#{self.class}: reloaded `#{file}'" - file - rescue LoadError, SyntaxError => ex - stderr.puts ex - ensure - @mtimes[file] = mtime - end - - module Stat - def rotation - files = [$0, *$LOADED_FEATURES].uniq - paths = ['./', *$LOAD_PATH].uniq - - files.map{|file| - next if file =~ /\.(so|bundle)$/ # cannot reload compiled files - - found, stat = figure_path(file, paths) - next unless found && stat && mtime = stat.mtime - - @cache[file] = found - - yield(found, mtime) - }.compact - end - - # Takes a relative or absolute +file+ name, a couple possible +paths+ that - # the +file+ might reside in. Returns the full path and File::Stat for the - # path. - def figure_path(file, paths) - found = @cache[file] - found = file if !found and Pathname.new(file).absolute? - found, stat = safe_stat(found) - return found, stat if found - - paths.find do |possible_path| - path = ::File.join(possible_path, file) - found, stat = safe_stat(path) - return ::File.expand_path(found), stat if found - end - end - - def safe_stat(file) - return unless file - stat = ::File.stat(file) - return file, stat if stat.file? - rescue Errno::ENOENT, Errno::ENOTDIR - @cache.delete(file) and false - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/request.rb deleted file mode 100644 index 4c4cf61..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/request.rb +++ /dev/null @@ -1,248 +0,0 @@ -require 'rack/utils' - -module Rack - # Rack::Request provides a convenient interface to a Rack - # environment. It is stateless, the environment +env+ passed to the - # constructor will be directly modified. - # - # req = Rack::Request.new(env) - # req.post? - # req.params["data"] - # - # The environment hash passed will store a reference to the Request object - # instantiated so that it will only instantiate if an instance of the Request - # object doesn't already exist. - - class Request - # The environment of the request. - attr_reader :env - - def initialize(env) - @env = env - end - - def body; @env["rack.input"] end - def scheme; @env["rack.url_scheme"] end - def script_name; @env["SCRIPT_NAME"].to_s end - def path_info; @env["PATH_INFO"].to_s end - def port; @env["SERVER_PORT"].to_i end - def request_method; @env["REQUEST_METHOD"] end - def query_string; @env["QUERY_STRING"].to_s end - def content_length; @env['CONTENT_LENGTH'] end - def content_type; @env['CONTENT_TYPE'] end - def session; @env['rack.session'] ||= {} end - def session_options; @env['rack.session.options'] ||= {} end - - # The media type (type/subtype) portion of the CONTENT_TYPE header - # without any media type parameters. e.g., when CONTENT_TYPE is - # "text/plain;charset=utf-8", the media-type is "text/plain". - # - # For more information on the use of media types in HTTP, see: - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 - def media_type - content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase - end - - # The media type parameters provided in CONTENT_TYPE as a Hash, or - # an empty Hash if no CONTENT_TYPE or media-type parameters were - # provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8", - # this method responds with the following Hash: - # { 'charset' => 'utf-8' } - def media_type_params - return {} if content_type.nil? - content_type.split(/\s*[;,]\s*/)[1..-1]. - collect { |s| s.split('=', 2) }. - inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash } - end - - # The character set of the request body if a "charset" media type - # parameter was given, or nil if no "charset" was specified. Note - # that, per RFC2616, text/* media types that specify no explicit - # charset are to be considered ISO-8859-1. - def content_charset - media_type_params['charset'] - end - - def host - # Remove port number. - (@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '') - end - - def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end - def path_info=(s); @env["PATH_INFO"] = s.to_s end - - def get?; request_method == "GET" end - def post?; request_method == "POST" end - def put?; request_method == "PUT" end - def delete?; request_method == "DELETE" end - def head?; request_method == "HEAD" end - - # The set of form-data media-types. Requests that do not indicate - # one of the media types presents in this list will not be eligible - # for form-data / param parsing. - FORM_DATA_MEDIA_TYPES = [ - nil, - 'application/x-www-form-urlencoded', - 'multipart/form-data' - ] - - # The set of media-types. Requests that do not indicate - # one of the media types presents in this list will not be eligible - # for param parsing like soap attachments or generic multiparts - PARSEABLE_DATA_MEDIA_TYPES = [ - 'multipart/related', - 'multipart/mixed' - ] - - # Determine whether the request body contains form-data by checking - # the request media_type against registered form-data media-types: - # "application/x-www-form-urlencoded" and "multipart/form-data". The - # list of form-data media types can be modified through the - # +FORM_DATA_MEDIA_TYPES+ array. - def form_data? - FORM_DATA_MEDIA_TYPES.include?(media_type) - end - - # Determine whether the request body contains data by checking - # the request media_type against registered parse-data media-types - def parseable_data? - PARSEABLE_DATA_MEDIA_TYPES.include?(media_type) - end - - # Returns the data recieved in the query string. - def GET - if @env["rack.request.query_string"] == query_string - @env["rack.request.query_hash"] - else - @env["rack.request.query_string"] = query_string - @env["rack.request.query_hash"] = - Utils.parse_nested_query(query_string) - end - end - - # Returns the data recieved in the request body. - # - # This method support both application/x-www-form-urlencoded and - # multipart/form-data. - def POST - if @env["rack.input"].nil? - raise "Missing rack.input" - elsif @env["rack.request.form_input"].eql? @env["rack.input"] - @env["rack.request.form_hash"] - elsif form_data? || parseable_data? - @env["rack.request.form_input"] = @env["rack.input"] - unless @env["rack.request.form_hash"] = - Utils::Multipart.parse_multipart(env) - form_vars = @env["rack.input"].read - - # Fix for Safari Ajax postings that always append \0 - form_vars.sub!(/\0\z/, '') - - @env["rack.request.form_vars"] = form_vars - @env["rack.request.form_hash"] = Utils.parse_nested_query(form_vars) - - @env["rack.input"].rewind - end - @env["rack.request.form_hash"] - else - {} - end - end - - # The union of GET and POST data. - def params - self.put? ? self.GET : self.GET.update(self.POST) - rescue EOFError => e - self.GET - end - - # shortcut for request.params[key] - def [](key) - params[key.to_s] - end - - # shortcut for request.params[key] = value - def []=(key, value) - params[key.to_s] = value - end - - # like Hash#values_at - def values_at(*keys) - keys.map{|key| params[key] } - end - - # the referer of the client or '/' - def referer - @env['HTTP_REFERER'] || '/' - end - alias referrer referer - - - def cookies - return {} unless @env["HTTP_COOKIE"] - - if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] - @env["rack.request.cookie_hash"] - else - @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] - # According to RFC 2109: - # If multiple cookies satisfy the criteria above, they are ordered in - # the Cookie header such that those with more specific Path attributes - # precede those with less specific. Ordering with respect to other - # attributes (e.g., Domain) is unspecified. - @env["rack.request.cookie_hash"] = - Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)| - h[k] = Array === v ? v.first : v - h - } - end - end - - def xhr? - @env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" - end - - # Tries to return a remake of the original request URL as a string. - def url - url = scheme + "://" - url << host - - if scheme == "https" && port != 443 || - scheme == "http" && port != 80 - url << ":#{port}" - end - - url << fullpath - - url - end - - def path - script_name + path_info - end - - def fullpath - query_string.empty? ? path : "#{path}?#{query_string}" - end - - def accept_encoding - @env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part| - m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick - - if m - [m[1], (m[2] || 1.0).to_f] - else - raise "Invalid value for Accept-Encoding: #{part.inspect}" - end - end - end - - def ip - if addr = @env['HTTP_X_FORWARDED_FOR'] - addr.split(',').last.strip - else - @env['REMOTE_ADDR'] - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/response.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/response.rb deleted file mode 100644 index 28b4d83..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/response.rb +++ /dev/null @@ -1,183 +0,0 @@ -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::Response provides a convenient interface to create a Rack - # response. - # - # It allows setting of headers and cookies, and provides useful - # defaults (a OK response containing HTML). - # - # You can use Response#write to iteratively generate your response, - # but note that this is buffered by Rack::Response until you call - # +finish+. +finish+ however can take a block inside which calls to - # +write+ are syncronous with the Rack response. - # - # Your application's +call+ should end returning Response#finish. - - class Response - attr_accessor :length - - def initialize(body=[], status=200, header={}, &block) - @status = status - @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}. - merge(header)) - - @writer = lambda { |x| @body << x } - @block = nil - @length = 0 - - @body = [] - - if body.respond_to? :to_str - write body.to_str - elsif body.respond_to?(:each) - body.each { |part| - write part.to_s - } - else - raise TypeError, "stringable or iterable required" - end - - yield self if block_given? - end - - attr_reader :header - attr_accessor :status, :body - - def [](key) - header[key] - end - - def []=(key, value) - header[key] = value - end - - def set_cookie(key, value) - case value - when Hash - domain = "; domain=" + value[:domain] if value[:domain] - path = "; path=" + value[:path] if value[:path] - # According to RFC 2109, we need dashes here. - # N.B.: cgi.rb uses spaces... - expires = "; expires=" + value[:expires].clone.gmtime. - strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires] - secure = "; secure" if value[:secure] - httponly = "; HttpOnly" if value[:httponly] - value = value[:value] - end - value = [value] unless Array === value - cookie = Utils.escape(key) + "=" + - value.map { |v| Utils.escape v }.join("&") + - "#{domain}#{path}#{expires}#{secure}#{httponly}" - - case self["Set-Cookie"] - when Array - self["Set-Cookie"] << cookie - when String - self["Set-Cookie"] = [self["Set-Cookie"], cookie] - when nil - self["Set-Cookie"] = cookie - end - end - - def delete_cookie(key, value={}) - unless Array === self["Set-Cookie"] - self["Set-Cookie"] = [self["Set-Cookie"]].compact - end - - self["Set-Cookie"].reject! { |cookie| - cookie =~ /\A#{Utils.escape(key)}=/ - } - - set_cookie(key, - {:value => '', :path => nil, :domain => nil, - :expires => Time.at(0) }.merge(value)) - end - - def redirect(target, status=302) - self.status = status - self["Location"] = target - end - - def finish(&block) - @block = block - - if [204, 304].include?(status.to_i) - header.delete "Content-Type" - [status.to_i, header.to_hash, []] - else - [status.to_i, header.to_hash, self] - end - end - alias to_a finish # For *response - - def each(&callback) - @body.each(&callback) - @writer = callback - @block.call(self) if @block - end - - # Append to body and update Content-Length. - # - # NOTE: Do not mix #write and direct #body access! - # - def write(str) - s = str.to_s - @length += Rack::Utils.bytesize(s) - @writer.call s - - header["Content-Length"] = @length.to_s - str - end - - def close - body.close if body.respond_to?(:close) - end - - def empty? - @block == nil && @body.empty? - end - - alias headers header - - module Helpers - def invalid?; @status < 100 || @status >= 600; end - - def informational?; @status >= 100 && @status < 200; end - def successful?; @status >= 200 && @status < 300; end - def redirection?; @status >= 300 && @status < 400; end - def client_error?; @status >= 400 && @status < 500; end - def server_error?; @status >= 500 && @status < 600; end - - def ok?; @status == 200; end - def forbidden?; @status == 403; end - def not_found?; @status == 404; end - - def redirect?; [301, 302, 303, 307].include? @status; end - def empty?; [201, 204, 304].include? @status; end - - # Headers - attr_reader :headers, :original_headers - - def include?(header) - !!headers[header] - end - - def content_type - headers["Content-Type"] - end - - def content_length - cl = headers["Content-Length"] - cl ? cl.to_i : cl - end - - def location - headers["Location"] - end - end - - include Helpers - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/rewindable_input.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/rewindable_input.rb deleted file mode 100644 index 9e9b21f..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/rewindable_input.rb +++ /dev/null @@ -1,98 +0,0 @@ -require 'tempfile' - -module Rack - # Class which can make any IO object rewindable, including non-rewindable ones. It does - # this by buffering the data into a tempfile, which is rewindable. - # - # rack.input is required to be rewindable, so if your input stream IO is non-rewindable - # by nature (e.g. a pipe or a socket) then you can wrap it in an object of this class - # to easily make it rewindable. - # - # Don't forget to call #close when you're done. This frees up temporary resources that - # RewindableInput uses, though it does *not* close the original IO object. - class RewindableInput - def initialize(io) - @io = io - @rewindable_io = nil - @unlinked = false - end - - def gets - make_rewindable unless @rewindable_io - @rewindable_io.gets - end - - def read(*args) - make_rewindable unless @rewindable_io - @rewindable_io.read(*args) - end - - def each(&block) - make_rewindable unless @rewindable_io - @rewindable_io.each(&block) - end - - def rewind - make_rewindable unless @rewindable_io - @rewindable_io.rewind - end - - # Closes this RewindableInput object without closing the originally - # wrapped IO oject. Cleans up any temporary resources that this RewindableInput - # has created. - # - # This method may be called multiple times. It does nothing on subsequent calls. - def close - if @rewindable_io - if @unlinked - @rewindable_io.close - else - @rewindable_io.close! - end - @rewindable_io = nil - end - end - - private - - # Ruby's Tempfile class has a bug. Subclass it and fix it. - class Tempfile < ::Tempfile - def _close - @tmpfile.close if @tmpfile - @data[1] = nil if @data - @tmpfile = nil - end - end - - def make_rewindable - # Buffer all data into a tempfile. Since this tempfile is private to this - # RewindableInput object, we chmod it so that nobody else can read or write - # it. On POSIX filesystems we also unlink the file so that it doesn't - # even have a file entry on the filesystem anymore, though we can still - # access it because we have the file handle open. - @rewindable_io = Tempfile.new('RackRewindableInput') - @rewindable_io.chmod(0000) - if filesystem_has_posix_semantics? - @rewindable_io.unlink - @unlinked = true - end - - buffer = "" - while @io.read(1024 * 4, buffer) - entire_buffer_written_out = false - while !entire_buffer_written_out - written = @rewindable_io.write(buffer) - entire_buffer_written_out = written == buffer.size - if !entire_buffer_written_out - buffer.slice!(0 .. written - 1) - end - end - end - @rewindable_io.rewind - end - - def filesystem_has_posix_semantics? - RUBY_PLATFORM !~ /(mswin|mingw|cygwin|java)/ - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/abstract/id.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/abstract/id.rb deleted file mode 100644 index 218144c..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/abstract/id.rb +++ /dev/null @@ -1,142 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net -# bugrep: Andreas Zehnder - -require 'time' -require 'rack/request' -require 'rack/response' - -module Rack - - module Session - - module Abstract - - # ID sets up a basic framework for implementing an id based sessioning - # service. Cookies sent to the client for maintaining sessions will only - # contain an id reference. Only #get_session and #set_session are - # required to be overwritten. - # - # All parameters are optional. - # * :key determines the name of the cookie, by default it is - # 'rack.session' - # * :path, :domain, :expire_after, :secure, and :httponly set the related - # cookie options as by Rack::Response#add_cookie - # * :defer will not set a cookie in the response. - # * :renew (implementation dependent) will prompt the generation of a new - # session id, and migration of data to be referenced at the new id. If - # :defer is set, it will be overridden and the cookie will be set. - # * :sidbits sets the number of bits in length that a generated session - # id will be. - # - # These options can be set on a per request basis, at the location of - # env['rack.session.options']. Additionally the id of the session can be - # found within the options hash at the key :id. It is highly not - # recommended to change its value. - # - # Is Rack::Utils::Context compatible. - - class ID - DEFAULT_OPTIONS = { - :path => '/', - :domain => nil, - :expire_after => nil, - :secure => false, - :httponly => true, - :defer => false, - :renew => false, - :sidbits => 128 - } - - attr_reader :key, :default_options - def initialize(app, options={}) - @app = app - @key = options[:key] || "rack.session" - @default_options = self.class::DEFAULT_OPTIONS.merge(options) - end - - def call(env) - context(env) - end - - def context(env, app=@app) - load_session(env) - status, headers, body = app.call(env) - commit_session(env, status, headers, body) - end - - private - - # Generate a new session id using Ruby #rand. The size of the - # session id is controlled by the :sidbits option. - # Monkey patch this to use custom methods for session id generation. - - def generate_sid - "%0#{@default_options[:sidbits] / 4}x" % - rand(2**@default_options[:sidbits] - 1) - end - - # Extracts the session id from provided cookies and passes it and the - # environment to #get_session. It then sets the resulting session into - # 'rack.session', and places options and session metadata into - # 'rack.session.options'. - - def load_session(env) - request = Rack::Request.new(env) - session_id = request.cookies[@key] - - begin - session_id, session = get_session(env, session_id) - env['rack.session'] = session - rescue - env['rack.session'] = Hash.new - end - - env['rack.session.options'] = @default_options. - merge(:id => session_id) - end - - # Acquires the session from the environment and the session id from - # the session options and passes them to #set_session. If successful - # and the :defer option is not true, a cookie will be added to the - # response with the session's id. - - def commit_session(env, status, headers, body) - session = env['rack.session'] - options = env['rack.session.options'] - session_id = options[:id] - - if not session_id = set_session(env, session_id, session, options) - env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.") - [status, headers, body] - elsif options[:defer] and not options[:renew] - env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE - [status, headers, body] - else - cookie = Hash.new - cookie[:value] = session_id - cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? - response = Rack::Response.new(body, status, headers) - response.set_cookie(@key, cookie.merge(options)) - response.to_a - end - end - - # All thread safety and session retrival proceedures should occur here. - # Should return [session_id, session]. - # If nil is provided as the session id, generation of a new valid id - # should occur within. - - def get_session(env, sid) - raise '#get_session not implemented.' - end - - # All thread safety and session storage proceedures should occur here. - # Should return true or false dependant on whether or not the session - # was saved or not. - def set_session(env, sid, session, options) - raise '#set_session not implemented.' - end - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/cookie.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/cookie.rb deleted file mode 100644 index eace9bd..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/cookie.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'openssl' -require 'rack/request' -require 'rack/response' - -module Rack - - module Session - - # Rack::Session::Cookie provides simple cookie based session management. - # The session is a Ruby Hash stored as base64 encoded marshalled data - # set to :key (default: rack.session). - # When the secret key is set, cookie data is checked for data integrity. - # - # Example: - # - # use Rack::Session::Cookie, :key => 'rack.session', - # :domain => 'foo.com', - # :path => '/', - # :expire_after => 2592000, - # :secret => 'change_me' - # - # All parameters are optional. - - class Cookie - - def initialize(app, options={}) - @app = app - @key = options[:key] || "rack.session" - @secret = options[:secret] - @default_options = {:domain => nil, - :path => "/", - :expire_after => nil}.merge(options) - end - - def call(env) - load_session(env) - status, headers, body = @app.call(env) - commit_session(env, status, headers, body) - end - - private - - def load_session(env) - request = Rack::Request.new(env) - session_data = request.cookies[@key] - - if @secret && session_data - session_data, digest = session_data.split("--") - session_data = nil unless digest == generate_hmac(session_data) - end - - begin - session_data = session_data.unpack("m*").first - session_data = Marshal.load(session_data) - env["rack.session"] = session_data - rescue - env["rack.session"] = Hash.new - end - - env["rack.session.options"] = @default_options.dup - end - - def commit_session(env, status, headers, body) - session_data = Marshal.dump(env["rack.session"]) - session_data = [session_data].pack("m*") - - if @secret - session_data = "#{session_data}--#{generate_hmac(session_data)}" - end - - if session_data.size > (4096 - @key.size) - env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.") - [status, headers, body] - else - options = env["rack.session.options"] - cookie = Hash.new - cookie[:value] = session_data - cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil? - response = Rack::Response.new(body, status, headers) - response.set_cookie(@key, cookie.merge(options)) - response.to_a - end - end - - def generate_hmac(data) - OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data) - end - - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/memcache.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/memcache.rb deleted file mode 100644 index 4a65cbf..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/memcache.rb +++ /dev/null @@ -1,109 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net - -require 'rack/session/abstract/id' -require 'memcache' - -module Rack - module Session - # Rack::Session::Memcache provides simple cookie based session management. - # Session data is stored in memcached. The corresponding session key is - # maintained in the cookie. - # You may treat Session::Memcache as you would Session::Pool with the - # following caveats. - # - # * Setting :expire_after to 0 would note to the Memcache server to hang - # onto the session data until it would drop it according to it's own - # specifications. However, the cookie sent to the client would expire - # immediately. - # - # Note that memcache does drop data before it may be listed to expire. For - # a full description of behaviour, please see memcache's documentation. - - class Memcache < Abstract::ID - attr_reader :mutex, :pool - DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \ - :namespace => 'rack:session', - :memcache_server => 'localhost:11211' - - def initialize(app, options={}) - super - - @mutex = Mutex.new - @pool = MemCache. - new @default_options[:memcache_server], @default_options - raise 'No memcache servers' unless @pool.servers.any?{|s|s.alive?} - end - - def generate_sid - loop do - sid = super - break sid unless @pool.get(sid, true) - end - end - - def get_session(env, sid) - session = @pool.get(sid) if sid - @mutex.lock if env['rack.multithread'] - unless sid and session - env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? - session = {} - sid = generate_sid - ret = @pool.add sid, session - raise "Session collision on '#{sid.inspect}'" unless /^STORED/ =~ ret - end - session.instance_variable_set('@old', {}.merge(session)) - return [sid, session] - rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted - warn "#{self} is unable to find server." - warn $!.inspect - return [ nil, {} ] - ensure - @mutex.unlock if env['rack.multithread'] - end - - def set_session(env, session_id, new_session, options) - expiry = options[:expire_after] - expiry = expiry.nil? ? 0 : expiry + 1 - - @mutex.lock if env['rack.multithread'] - session = @pool.get(session_id) || {} - if options[:renew] or options[:drop] - @pool.delete session_id - return false if options[:drop] - session_id = generate_sid - @pool.add session_id, 0 # so we don't worry about cache miss on #set - end - old_session = new_session.instance_variable_get('@old') || {} - session = merge_sessions session_id, old_session, new_session, session - @pool.set session_id, session, expiry - return session_id - rescue MemCache::MemCacheError, Errno::ECONNREFUSED # MemCache server cannot be contacted - warn "#{self} is unable to find server." - warn $!.inspect - return false - ensure - @mutex.unlock if env['rack.multithread'] - end - - private - - def merge_sessions sid, old, new, cur=nil - cur ||= {} - unless Hash === old and Hash === new - warn 'Bad old or new sessions provided.' - return cur - end - - delete = old.keys - new.keys - warn "//@#{sid}: delete #{delete*','}" if $VERBOSE and not delete.empty? - delete.each{|k| cur.delete k } - - update = new.keys.select{|k| new[k] != old[k] } - warn "//@#{sid}: update #{update*','}" if $VERBOSE and not update.empty? - update.each{|k| cur[k] = new[k] } - - cur - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/pool.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/pool.rb deleted file mode 100644 index f6f8740..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/session/pool.rb +++ /dev/null @@ -1,100 +0,0 @@ -# AUTHOR: blink ; blink#ruby-lang@irc.freenode.net -# THANKS: -# apeiros, for session id generation, expiry setup, and threadiness -# sergio, threadiness and bugreps - -require 'rack/session/abstract/id' -require 'thread' - -module Rack - module Session - # Rack::Session::Pool provides simple cookie based session management. - # Session data is stored in a hash held by @pool. - # In the context of a multithreaded environment, sessions being - # committed to the pool is done in a merging manner. - # - # The :drop option is available in rack.session.options if you with to - # explicitly remove the session from the session cache. - # - # Example: - # myapp = MyRackApp.new - # sessioned = Rack::Session::Pool.new(myapp, - # :domain => 'foo.com', - # :expire_after => 2592000 - # ) - # Rack::Handler::WEBrick.run sessioned - - class Pool < Abstract::ID - attr_reader :mutex, :pool - DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false - - def initialize(app, options={}) - super - @pool = Hash.new - @mutex = Mutex.new - end - - def generate_sid - loop do - sid = super - break sid unless @pool.key? sid - end - end - - def get_session(env, sid) - session = @pool[sid] if sid - @mutex.lock if env['rack.multithread'] - unless sid and session - env['rack.errors'].puts("Session '#{sid.inspect}' not found, initializing...") if $VERBOSE and not sid.nil? - session = {} - sid = generate_sid - @pool.store sid, session - end - session.instance_variable_set('@old', {}.merge(session)) - return [sid, session] - ensure - @mutex.unlock if env['rack.multithread'] - end - - def set_session(env, session_id, new_session, options) - @mutex.lock if env['rack.multithread'] - session = @pool[session_id] - if options[:renew] or options[:drop] - @pool.delete session_id - return false if options[:drop] - session_id = generate_sid - @pool.store session_id, 0 - end - old_session = new_session.instance_variable_get('@old') || {} - session = merge_sessions session_id, old_session, new_session, session - @pool.store session_id, session - return session_id - rescue - warn "#{new_session.inspect} has been lost." - warn $!.inspect - ensure - @mutex.unlock if env['rack.multithread'] - end - - private - - def merge_sessions sid, old, new, cur=nil - cur ||= {} - unless Hash === old and Hash === new - warn 'Bad old or new sessions provided.' - return cur - end - - delete = old.keys - new.keys - warn "//@#{sid}: dropping #{delete*','}" if $DEBUG and not delete.empty? - delete.each{|k| cur.delete k } - - update = new.keys.select{|k| new[k] != old[k] } - warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty? - update.each{|k| cur[k] = new[k] } - - cur - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showexceptions.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showexceptions.rb deleted file mode 100644 index 697bc41..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showexceptions.rb +++ /dev/null @@ -1,349 +0,0 @@ -require 'ostruct' -require 'erb' -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::ShowExceptions catches all exceptions raised from the app it - # wraps. It shows a useful backtrace with the sourcefile and - # clickable context, the whole Rack environment and the request - # data. - # - # Be careful when you use this on public-facing sites as it could - # reveal information helpful to attackers. - - class ShowExceptions - CONTEXT = 7 - - def initialize(app) - @app = app - @template = ERB.new(TEMPLATE) - end - - def call(env) - @app.call(env) - rescue StandardError, LoadError, SyntaxError => e - backtrace = pretty(env, e) - [500, - {"Content-Type" => "text/html", - "Content-Length" => backtrace.join.size.to_s}, - backtrace] - end - - def pretty(env, exception) - req = Rack::Request.new(env) - path = (req.script_name + req.path_info).squeeze("/") - - frames = exception.backtrace.map { |line| - frame = OpenStruct.new - if line =~ /(.*?):(\d+)(:in `(.*)')?/ - frame.filename = $1 - frame.lineno = $2.to_i - frame.function = $4 - - begin - lineno = frame.lineno-1 - lines = ::File.readlines(frame.filename) - frame.pre_context_lineno = [lineno-CONTEXT, 0].max - frame.pre_context = lines[frame.pre_context_lineno...lineno] - frame.context_line = lines[lineno].chomp - frame.post_context_lineno = [lineno+CONTEXT, lines.size].min - frame.post_context = lines[lineno+1..frame.post_context_lineno] - rescue - end - - frame - else - nil - end - }.compact - - env["rack.errors"].puts "#{exception.class}: #{exception.message}" - env["rack.errors"].puts exception.backtrace.map { |l| "\t" + l } - env["rack.errors"].flush - - [@template.result(binding)] - end - - def h(obj) # :nodoc: - case obj - when String - Utils.escape_html(obj) - else - Utils.escape_html(obj.inspect) - end - end - - # :stopdoc: - -# adapted from Django -# Copyright (c) 2005, the Lawrence Journal-World -# Used under the modified BSD license: -# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 -TEMPLATE = <<'HTML' - - - - - - <%=h exception.class %> at <%=h path %> - - - - - -
-

<%=h exception.class %> at <%=h path %>

-

<%=h exception.message %>

- - - - - - -
Ruby<%=h frames.first.filename %>: in <%=h frames.first.function %>, line <%=h frames.first.lineno %>
Web<%=h req.request_method %> <%=h(req.host + path)%>
- -

Jump to:

- -
- -
-

Traceback (innermost first)

-
    -<% frames.each { |frame| %> -
  • - <%=h frame.filename %>: in <%=h frame.function %> - - <% if frame.context_line %> -
    - <% if frame.pre_context %> -
      - <% frame.pre_context.each { |line| %> -
    1. <%=h line %>
    2. - <% } %> -
    - <% end %> - -
      -
    1. <%=h frame.context_line %>...
    - - <% if frame.post_context %> -
      - <% frame.post_context.each { |line| %> -
    1. <%=h line %>
    2. - <% } %> -
    - <% end %> -
    - <% end %> -
  • -<% } %> -
-
- -
-

Request information

- -

GET

- <% unless req.GET.empty? %> - - - - - - - - - <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val.inspect %>
- <% else %> -

No GET data.

- <% end %> - -

POST

- <% unless req.POST.empty? %> - - - - - - - - - <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val.inspect %>
- <% else %> -

No POST data.

- <% end %> - - - - <% unless req.cookies.empty? %> - - - - - - - - - <% req.cookies.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val.inspect %>
- <% else %> -

No cookie data.

- <% end %> - -

Rack ENV

- - - - - - - - - <% env.sort_by { |k, v| k.to_s }.each { |key, val| %> - - - - - <% } %> - -
VariableValue
<%=h key %>
<%=h val %>
- -
- -
-

- You're seeing this error because you use Rack::ShowExceptions. -

-
- - - -HTML - - # :startdoc: - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showstatus.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showstatus.rb deleted file mode 100644 index 28258c7..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/showstatus.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'erb' -require 'rack/request' -require 'rack/utils' - -module Rack - # Rack::ShowStatus catches all empty responses the app it wraps and - # replaces them with a site explaining the error. - # - # Additional details can be put into rack.showstatus.detail - # and will be shown as HTML. If such details exist, the error page - # is always rendered, even if the reply was not empty. - - class ShowStatus - def initialize(app) - @app = app - @template = ERB.new(TEMPLATE) - end - - def call(env) - status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) - empty = headers['Content-Length'].to_i <= 0 - - # client or server error, or explicit message - if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"] - req = Rack::Request.new(env) - message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s - detail = env["rack.showstatus.detail"] || message - body = @template.result(binding) - size = Rack::Utils.bytesize(body) - [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]] - else - [status, headers, body] - end - end - - def h(obj) # :nodoc: - case obj - when String - Utils.escape_html(obj) - else - Utils.escape_html(obj.inspect) - end - end - - # :stopdoc: - -# adapted from Django -# Copyright (c) 2005, the Lawrence Journal-World -# Used under the modified BSD license: -# http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 -TEMPLATE = <<'HTML' - - - - - <%=h message %> at <%=h req.script_name + req.path_info %> - - - - -
-

<%=h message %> (<%= status.to_i %>)

- - - - - - - - - -
Request Method:<%=h req.request_method %>
Request URL:<%=h req.url %>
-
-
-

<%= detail %>

-
- -
-

- You're seeing this error because you use Rack::ShowStatus. -

-
- - -HTML - - # :startdoc: - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/static.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/static.rb deleted file mode 100644 index 168e8f8..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/static.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Rack - - # The Rack::Static middleware intercepts requests for static files - # (javascript files, images, stylesheets, etc) based on the url prefixes - # passed in the options, and serves them using a Rack::File object. This - # allows a Rack stack to serve both static and dynamic content. - # - # Examples: - # use Rack::Static, :urls => ["/media"] - # will serve all requests beginning with /media from the "media" folder - # located in the current directory (ie media/*). - # - # use Rack::Static, :urls => ["/css", "/images"], :root => "public" - # will serve all requests beginning with /css or /images from the folder - # "public" in the current directory (ie public/css/* and public/images/*) - - class Static - - def initialize(app, options={}) - @app = app - @urls = options[:urls] || ["/favicon.ico"] - root = options[:root] || Dir.pwd - @file_server = Rack::File.new(root) - end - - def call(env) - path = env["PATH_INFO"] - can_serve = @urls.any? { |url| path.index(url) == 0 } - - if can_serve - @file_server.call(env) - else - @app.call(env) - end - end - - end -end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/urlmap.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/urlmap.rb deleted file mode 100644 index fcf6616..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/urlmap.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Rack - # Rack::URLMap takes a hash mapping urls or paths to apps, and - # dispatches accordingly. Support for HTTP/1.1 host names exists if - # the URLs start with http:// or https://. - # - # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part - # relevant for dispatch is in the SCRIPT_NAME, and the rest in the - # PATH_INFO. This should be taken care of when you need to - # reconstruct the URL in order to create links. - # - # URLMap dispatches in such a way that the longest paths are tried - # first, since they are most specific. - - class URLMap - def initialize(map = {}) - remap(map) - end - - def remap(map) - @mapping = map.map { |location, app| - if location =~ %r{\Ahttps?://(.*?)(/.*)} - host, location = $1, $2 - else - host = nil - end - - unless location[0] == ?/ - raise ArgumentError, "paths need to start with /" - end - location = location.chomp('/') - - [host, location, app] - }.sort_by { |(h, l, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first - end - - def call(env) - path = env["PATH_INFO"].to_s.squeeze("/") - script_name = env['SCRIPT_NAME'] - hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT') - @mapping.each { |host, location, app| - next unless (hHost == host || sName == host \ - || (host.nil? && (hHost == sName || hHost == sName+':'+sPort))) - next unless location == path[0, location.size] - next unless path[location.size] == nil || path[location.size] == ?/ - - return app.call( - env.merge( - 'SCRIPT_NAME' => (script_name + location), - 'PATH_INFO' => path[location.size..-1])) - } - [404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]] - end - end -end - diff --git a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/utils.rb b/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/utils.rb deleted file mode 100644 index 228488c..0000000 --- a/actionpack/lib/action_controller/vendor/rack-1.0.x/rack/utils.rb +++ /dev/null @@ -1,520 +0,0 @@ -# -*- encoding: binary -*- - -require 'set' -require 'tempfile' - -module Rack - # Rack::Utils contains a grab-bag of useful methods for writing web - # applications adopted from all kinds of Ruby libraries. - - module Utils - # Performs URI escaping so that you can construct proper - # query strings faster. Use this rather than the cgi.rb - # version since it's faster. (Stolen from Camping). - def escape(s) - s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) { - '%'+$1.unpack('H2'*$1.size).join('%').upcase - }.tr(' ', '+') - end - module_function :escape - - # Unescapes a URI escaped string. (Stolen from Camping). - def unescape(s) - s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ - [$1.delete('%')].pack('H*') - } - end - module_function :unescape - - DEFAULT_SEP = /[&;] */n - - # Stolen from Mongrel, with some small modifications: - # Parses a query string by breaking it up at the '&' - # and ';' characters. You can also use this to parse - # cookies by changing the characters used in the second - # parameter (which defaults to '&;'). - def parse_query(qs, d = nil) - params = {} - - (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| - k, v = p.split('=', 2).map { |x| unescape(x) } - - if cur = params[k] - if cur.class == Array - params[k] << v - else - params[k] = [cur, v] - end - else - params[k] = v - end - end - - return params - end - module_function :parse_query - - def parse_nested_query(qs, d = nil) - params = {} - - (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| - k, v = unescape(p).split('=', 2) - normalize_params(params, k, v) - end - - return params - end - module_function :parse_nested_query - - def normalize_params(params, name, v = nil) - name =~ %r(\A[\[\]]*([^\[\]]+)\]*) - k = $1 || '' - after = $' || '' - - return if k.empty? - - if after == "" - params[k] = v - elsif after == "[]" - params[k] ||= [] - raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - params[k] << v - elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) - child_key = $1 - params[k] ||= [] - raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) - normalize_params(params[k].last, child_key, v) - else - params[k] << normalize_params({}, child_key, v) - end - else - params[k] ||= {} - raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) - params[k] = normalize_params(params[k], after, v) - end - - return params - end - module_function :normalize_params - - def build_query(params) - params.map { |k, v| - if v.class == Array - build_query(v.map { |x| [k, x] }) - else - "#{escape(k)}=#{escape(v)}" - end - }.join("&") - end - module_function :build_query - - def build_nested_query(value, prefix = nil) - case value - when Array - value.map { |v| - build_nested_query(v, "#{prefix}[]") - }.join("&") - when Hash - value.map { |k, v| - build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) - }.join("&") - when String - raise ArgumentError, "value must be a Hash" if prefix.nil? - "#{prefix}=#{escape(value)}" - else - prefix - end - end - module_function :build_nested_query - - # Escape ampersands, brackets and quotes to their HTML/XML entities. - def escape_html(string) - string.to_s.gsub("&", "&"). - gsub("<", "<"). - gsub(">", ">"). - gsub("'", "'"). - gsub('"', """) - end - module_function :escape_html - - def select_best_encoding(available_encodings, accept_encoding) - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html - - expanded_accept_encoding = - accept_encoding.map { |m, q| - if m == "*" - (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] } - else - [[m, q]] - end - }.inject([]) { |mem, list| - mem + list - } - - encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m } - - unless encoding_candidates.include?("identity") - encoding_candidates.push("identity") - end - - expanded_accept_encoding.find_all { |m, q| - q == 0.0 - }.each { |m, _| - encoding_candidates.delete(m) - } - - return (encoding_candidates & available_encodings)[0] - end - module_function :select_best_encoding - - # Return the bytesize of String; uses String#length under Ruby 1.8 and - # String#bytesize under 1.9. - if ''.respond_to?(:bytesize) - def bytesize(string) - string.bytesize - end - else - def bytesize(string) - string.size - end - end - module_function :bytesize - - # Context allows the use of a compatible middleware at different points - # in a request handling stack. A compatible middleware must define - # #context which should take the arguments env and app. The first of which - # would be the request environment. The second of which would be the rack - # application that the request would be forwarded to. - class Context - attr_reader :for, :app - - def initialize(app_f, app_r) - raise 'running context does not respond to #context' unless app_f.respond_to? :context - @for, @app = app_f, app_r - end - - def call(env) - @for.context(env, @app) - end - - def recontext(app) - self.class.new(@for, app) - end - - def context(env, app=@app) - recontext(app).call(env) - end - end - - # A case-insensitive Hash that preserves the original case of a - # header when set. - class HeaderHash < Hash - def initialize(hash={}) - @names = {} - hash.each { |k, v| self[k] = v } - end - - def to_hash - inject({}) do |hash, (k,v)| - if v.respond_to? :to_ary - hash[k] = v.to_ary.join("\n") - else - hash[k] = v - end - hash - end - end - - def [](k) - super(@names[k] ||= @names[k.downcase]) - end - - def []=(k, v) - delete k - @names[k] = @names[k.downcase] = k - super k, v - end - - def delete(k) - canonical = k.downcase - super @names.delete(canonical) - @names.delete_if { |name,| name.downcase == canonical } - end - - def include?(k) - @names.include?(k) || @names.include?(k.downcase) - end - - alias_method :has_key?, :include? - alias_method :member?, :include? - alias_method :key?, :include? - - def merge!(other) - other.each { |k, v| self[k] = v } - self - end - - def merge(other) - hash = dup - hash.merge! other - end - end - - # Every standard HTTP code mapped to the appropriate message. - # Stolen from Mongrel. - HTTP_STATUS_CODES = { - 100 => 'Continue', - 101 => 'Switching Protocols', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Large', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported' - } - - # Responses with HTTP status codes that should not have an entity body - STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304) - - # A multipart form data parser, adapted from IOWA. - # - # Usually, Rack::Request#POST takes care of calling this. - - module Multipart - class UploadedFile - # The filename, *not* including the path, of the "uploaded" file - attr_reader :original_filename - - # The content type of the "uploaded" file - attr_accessor :content_type - - def initialize(path, content_type = "text/plain", binary = false) - raise "#{path} file does not exist" unless ::File.exist?(path) - @content_type = content_type - @original_filename = ::File.basename(path) - @tempfile = Tempfile.new(@original_filename) - @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding) - @tempfile.binmode if binary - FileUtils.copy_file(path, @tempfile.path) - end - - def path - @tempfile.path - end - alias_method :local_path, :path - - def method_missing(method_name, *args, &block) #:nodoc: - @tempfile.__send__(method_name, *args, &block) - end - end - - EOL = "\r\n" - MULTIPART_BOUNDARY = "AaB03x" - - def self.parse_multipart(env) - unless env['CONTENT_TYPE'] =~ - %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n - nil - else - boundary = "--#{$1}" - - params = {} - buf = "" - content_length = env['CONTENT_LENGTH'].to_i - input = env['rack.input'] - input.rewind - - boundary_size = Utils.bytesize(boundary) + EOL.size - bufsize = 16384 - - content_length -= boundary_size - - read_buffer = '' - - status = input.read(boundary_size, read_buffer) - raise EOFError, "bad content body" unless status == boundary + EOL - - rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n - - loop { - head = nil - body = '' - filename = content_type = name = nil - - until head && buf =~ rx - if !head && i = buf.index(EOL+EOL) - head = buf.slice!(0, i+2) # First \r\n - buf.slice!(0, 2) # Second \r\n - - filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1] - content_type = head[/Content-Type: (.*)#{EOL}/ni, 1] - name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1] - - if content_type || filename - body = Tempfile.new("RackMultipart") - body.binmode if body.respond_to?(:binmode) - end - - next - end - - # Save the read body part. - if head && (boundary_size+4 < buf.size) - body << buf.slice!(0, buf.size - (boundary_size+4)) - end - - c = input.read(bufsize < content_length ? bufsize : content_length, read_buffer) - raise EOFError, "bad content body" if c.nil? || c.empty? - buf << c - content_length -= c.size - end - - # Save the rest. - if i = buf.index(rx) - body << buf.slice!(0, i) - buf.slice!(0, boundary_size+2) - - content_length = -1 if $1 == "--" - end - - if filename == "" - # filename is blank which means no file has been selected - data = nil - elsif filename - body.rewind - - # Take the basename of the upload's original filename. - # This handles the full Windows paths given by Internet Explorer - # (and perhaps other broken user agents) without affecting - # those which give the lone filename. - filename =~ /^(?:.*[:\\\/])?(.*)/m - filename = $1 - - data = {:filename => filename, :type => content_type, - :name => name, :tempfile => body, :head => head} - elsif !filename && content_type - body.rewind - - # Generic multipart cases, not coming from a form - data = {:type => content_type, - :name => name, :tempfile => body, :head => head} - else - data = body - end - - Utils.normalize_params(params, name, data) unless data.nil? - - break if buf.empty? || content_length == -1 - } - - input.rewind - - params - end - end - - def self.build_multipart(params, first = true) - if first - unless params.is_a?(Hash) - raise ArgumentError, "value must be a Hash" - end - - multipart = false - query = lambda { |value| - case value - when Array - value.each(&query) - when Hash - value.values.each(&query) - when UploadedFile - multipart = true - end - } - params.values.each(&query) - return nil unless multipart - end - - flattened_params = Hash.new - - params.each do |key, value| - k = first ? key.to_s : "[#{key}]" - - case value - when Array - value.map { |v| - build_multipart(v, false).each { |subkey, subvalue| - flattened_params["#{k}[]#{subkey}"] = subvalue - } - } - when Hash - build_multipart(value, false).each { |subkey, subvalue| - flattened_params[k + subkey] = subvalue - } - else - flattened_params[k] = value - end - end - - if first - flattened_params.map { |name, file| - if file.respond_to?(:original_filename) - ::File.open(file.path, "rb") do |f| - f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding) -<<-EOF ---#{MULTIPART_BOUNDARY}\r -Content-Disposition: form-data; name="#{name}"; filename="#{Utils.escape(file.original_filename)}"\r -Content-Type: #{file.content_type}\r -Content-Length: #{::File.stat(file.path).size}\r -\r -#{f.read}\r -EOF - end - else -<<-EOF ---#{MULTIPART_BOUNDARY}\r -Content-Disposition: form-data; name="#{name}"\r -\r -#{file}\r -EOF - end - }.join + "--#{MULTIPART_BOUNDARY}--\r" - else - flattened_params - end - end - end - end -end -- 1.6.4.2 From ae85927ea84a0e16d2fe9ba156de537b7935a943 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 12 Jul 2009 19:31:41 -0500 Subject: [PATCH 144/779] Correctly setup the rack gem dependency. --- actionpack/Rakefile | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/actionpack/Rakefile b/actionpack/Rakefile index f43ca4d..17f6f1a 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -80,6 +80,7 @@ spec = Gem::Specification.new do |s| s.requirements << 'none' s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD) + s.add_dependency('rack', '~> 1.0.0') s.require_path = 'lib' s.autorequire = 'action_controller' -- 1.6.4.2 From b3ec7b2d03a52e43a4451d522eea7e6499289daa Mon Sep 17 00:00:00 2001 From: Szymon Nowak Date: Wed, 15 Jul 2009 22:29:29 +0200 Subject: [PATCH 145/779] Add primary_key option to belongs_to association [#765 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/CHANGELOG | 4 + activerecord/lib/active_record/associations.rb | 9 +- .../associations/belongs_to_association.rb | 26 ++++- .../belongs_to_polymorphic_association.rb | 6 +- .../lib/active_record/autosave_association.rb | 3 +- .../associations/belongs_to_associations_test.rb | 106 +++++++++++++++++++- activerecord/test/cases/base_test.rb | 2 +- activerecord/test/cases/reflection_test.rb | 10 +- activerecord/test/models/author.rb | 2 + activerecord/test/models/company.rb | 1 + activerecord/test/models/essay.rb | 3 + activerecord/test/models/reply.rb | 3 +- activerecord/test/models/topic.rb | 1 + activerecord/test/schema/schema.rb | 7 ++ 14 files changed, 163 insertions(+), 20 deletions(-) create mode 100644 activerecord/test/models/essay.rb diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index d820b2c..5593a22 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,9 @@ *2.3.3 (July 12, 2009)* +* Added :primary_key option to belongs_to associations. #765 [Szymon Nowak, Philip Hallstrom, Noel Rocha] + # employees.company_name references companies.name + Employee.belongs_to :company, :primary_key => 'name', :foreign_key => 'company_name' + * 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 8491a26..66db63a 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -957,6 +957,8 @@ module ActiveRecord # of the association with an "_id" suffix. So a class that defines a belongs_to :person association will use # "person_id" as the default :foreign_key. Similarly, belongs_to :favorite_person, :class_name => "Person" # will use a foreign key of "favorite_person_id". + # [:primary_key] + # Specify the method that returns the primary key of associated object used for the association. By default this is id. # [:dependent] # If set to :destroy, the associated object is destroyed when this object is. If set to # :delete, the associated object is deleted *without* calling its destroy method. This option should not be specified when @@ -987,6 +989,7 @@ module ActiveRecord # # Option examples: # belongs_to :firm, :foreign_key => "client_of" + # belongs_to :person, :primary_key => "name", :foreign_key => "person_name" # belongs_to :author, :class_name => "Person", :foreign_key => "author_id" # belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id", # :conditions => 'discounts > #{payments_count}' @@ -1320,14 +1323,14 @@ module ActiveRecord method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym define_method(method_name) do association = send(reflection.name) - association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil? + association.class.increment_counter(cache_column, association.id) unless association.nil? end after_create(method_name) method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym define_method(method_name) do association = send(reflection.name) - association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil? + association.class.decrement_counter(cache_column, association.id) unless association.nil? end before_destroy(method_name) @@ -1519,7 +1522,7 @@ module ActiveRecord mattr_accessor :valid_keys_for_belongs_to_association @@valid_keys_for_belongs_to_association = [ - :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, + :class_name, :primary_key, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent, :counter_cache, :extend, :polymorphic, :readonly, :validate, :touch ] diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index f05c6be..05f8f4f 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -14,7 +14,7 @@ module ActiveRecord if record.nil? if counter_cache_name && !@owner.new_record? - @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name] + @reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name] end @target = @owner[@reflection.primary_key_name] = nil @@ -27,7 +27,7 @@ module ActiveRecord end @target = (AssociationProxy === record ? record.target : record) - @owner[@reflection.primary_key_name] = record.id unless record.new_record? + @owner[@reflection.primary_key_name] = record_id(record) unless record.new_record? @updated = true end @@ -41,18 +41,36 @@ module ActiveRecord private def find_target - @reflection.klass.find( + find_method = if @reflection.options[:primary_key] + "find_by_#{@reflection.options[:primary_key]}" + else + "find" + end + @reflection.klass.send(find_method, @owner[@reflection.primary_key_name], :select => @reflection.options[:select], :conditions => conditions, :include => @reflection.options[:include], :readonly => @reflection.options[:readonly] - ) + ) if @owner[@reflection.primary_key_name] end def foreign_key_present !@owner[@reflection.primary_key_name].nil? end + + def record_id(record) + record.send(@reflection.options[:primary_key] || :id) + end + + def previous_record_id + @previous_record_id ||= if @reflection.options[:primary_key] + previous_record = @owner.send(@reflection.name) + previous_record.nil? ? nil : previous_record.id + else + @owner[@reflection.primary_key_name] + end + end end end end diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb index d8146da..67e18d6 100644 --- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb @@ -7,7 +7,7 @@ module ActiveRecord else @target = (AssociationProxy === record ? record.target : record) - @owner[@reflection.primary_key_name] = record.id + @owner[@reflection.primary_key_name] = record_id(record) @owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s @updated = true @@ -41,6 +41,10 @@ module ActiveRecord !@owner[@reflection.primary_key_name].nil? end + def record_id(record) + record.send(@reflection.options[:primary_key] || :id) + end + def association_class @owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil end diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 9717ca3..4f63b52 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -340,7 +340,8 @@ module ActiveRecord association.save(!autosave) if association.new_record? || autosave if association.updated? - self[reflection.primary_key_name] = association.id + association_id = association.send(reflection.options[:primary_key] || :id) + self[reflection.primary_key_name] = association_id # TODO: Removing this code doesn't seem to matter… if reflection.options[:polymorphic] self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 13a78a1..970601c 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -14,6 +14,7 @@ require 'models/tagging' require 'models/comment' require 'models/sponsor' require 'models/member' +require 'models/essay' class BelongsToAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :topics, @@ -25,6 +26,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert !Client.find(3).firm.nil?, "Microsoft should have a firm" end + def test_belongs_to_with_primary_key + client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name) + assert_equal companies(:first_firm).name, client.firm_with_primary_key.name + end + def test_proxy_assignment account = Account.find(1) assert_nothing_raised { account.firm = account.firm } @@ -47,6 +53,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal apple.id, citibank.firm_id end + def test_natural_assignment_with_primary_key + apple = Firm.create("name" => "Apple") + citibank = Client.create("name" => "Primary key client") + citibank.firm_with_primary_key = apple + assert_equal apple.name, citibank.firm_name + end + def test_no_unexpected_aliasing first_firm = companies(:first_firm) another_firm = companies(:another_firm) @@ -69,6 +82,15 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal apple, citibank.firm end + def test_creating_the_belonging_object_with_primary_key + client = Client.create(:name => "Primary key client") + apple = client.create_firm_with_primary_key("name" => "Apple") + assert_equal apple, client.firm_with_primary_key + client.save + client.reload + assert_equal apple, client.firm_with_primary_key + end + def test_building_the_belonging_object citibank = Account.create("credit_limit" => 10) apple = citibank.build_firm("name" => "Apple") @@ -76,6 +98,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal apple.id, citibank.firm_id end + def test_building_the_belonging_object_with_primary_key + client = Client.create(:name => "Primary key client") + apple = client.build_firm_with_primary_key("name" => "Apple") + client.save + assert_equal apple.name, client.firm_name + end + def test_natural_assignment_to_nil client = Client.find(3) client.firm = nil @@ -84,6 +113,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_nil client.client_of end + def test_natural_assignment_to_nil_with_primary_key + client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name) + client.firm_with_primary_key = nil + client.save + assert_nil client.firm_with_primary_key(true) + assert_nil client.client_of + end + def test_with_different_class_name assert_equal Company.find(1).name, Company.find(3).firm_with_other_name.name assert_not_nil Company.find(3).firm_with_other_name, "Microsoft should have a firm" @@ -110,6 +147,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted" end + def test_belongs_to_with_primary_key_counter + debate = Topic.create("title" => "debate") + assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet" + + trash = debate.replies_with_primary_key.create("title" => "blah!", "content" => "world around!") + assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply created" + + trash.destroy + assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted" + end + def test_belongs_to_counter_with_assigning_nil p = Post.find(1) c = Comment.find(1) @@ -122,6 +170,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal 1, Post.find(p.id).comments.size end + def test_belongs_to_with_primary_key_counter_with_assigning_nil + debate = Topic.create("title" => "debate") + reply = Reply.create("title" => "blah!", "content" => "world around!", "parent_title" => "debate") + + assert_equal debate.title, reply.parent_title + assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count") + + reply.topic_with_primary_key = nil + + assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count") + end + def test_belongs_to_counter_with_reassigning t1 = Topic.create("title" => "t1") t2 = Topic.create("title" => "t2") @@ -219,6 +279,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal firm, final_cut.firm(true) end + def test_assignment_before_child_saved_with_primary_key + final_cut = Client.new("name" => "Final Cut") + firm = Firm.find(1) + final_cut.firm_with_primary_key = firm + assert final_cut.new_record? + assert final_cut.save + assert !final_cut.new_record? + assert !firm.new_record? + assert_equal firm, final_cut.firm_with_primary_key + assert_equal firm, final_cut.firm_with_primary_key(true) + end + def test_new_record_with_foreign_key_but_no_object c = Client.new("firm_id" => 1) assert_equal Firm.find(:first), c.firm_with_basic_id @@ -297,26 +369,52 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase member = Member.create sponsor.sponsorable = member assert_equal "Member", sponsor.sponsorable_type - + # should update when assigning a new record sponsor = Sponsor.new member = Member.new sponsor.sponsorable = member assert_equal "Member", sponsor.sponsorable_type end - + + def test_polymorphic_assignment_with_primary_key_foreign_type_field_updating + # should update when assigning a saved record + essay = Essay.new + writer = Author.create(:name => "David") + essay.writer = writer + assert_equal "Author", essay.writer_type + + # should update when assigning a new record + essay = Essay.new + writer = Author.new + essay.writer = writer + assert_equal "Author", essay.writer_type + end + def test_polymorphic_assignment_updates_foreign_id_field_for_new_and_saved_records sponsor = Sponsor.new saved_member = Member.create new_member = Member.new - + sponsor.sponsorable = saved_member assert_equal saved_member.id, sponsor.sponsorable_id - + sponsor.sponsorable = new_member assert_equal nil, sponsor.sponsorable_id end + def test_polymorphic_assignment_with_primary_key_updates_foreign_id_field_for_new_and_saved_records + essay = Essay.new + saved_writer = Author.create(:name => "David") + new_writer = Author.new + + essay.writer = saved_writer + assert_equal saved_writer.name, essay.writer_id + + essay.writer = new_writer + assert_equal nil, essay.writer_id + end + def test_belongs_to_proxy_should_not_respond_to_private_methods assert_raise(NoMethodError) { companies(:first_firm).private_method } assert_raise(NoMethodError) { companies(:second_client).firm.private_method } diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 43cda45..604271e 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -2014,7 +2014,7 @@ class BasicsTest < ActiveRecord::TestCase def test_inspect_instance topic = topics(:first) - assert_equal %(#), topic.inspect + assert_equal %(#), topic.inspect end def test_inspect_new_instance diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index db64bbb..30ec157 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -21,25 +21,25 @@ class ReflectionTest < ActiveRecord::TestCase def test_read_attribute_names assert_equal( - %w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id type ).sort, + %w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id parent_title type ).sort, @first.attribute_names ) end def test_columns - assert_equal 12, Topic.columns.length + assert_equal 13, Topic.columns.length end def test_columns_are_returned_in_the_order_they_were_declared column_names = Topic.columns.map { |column| column.name } - assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id type), column_names + assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type), column_names end def test_content_columns content_columns = Topic.content_columns content_column_names = content_columns.map {|column| column.name} - assert_equal 8, content_columns.length - assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved).sort, content_column_names.sort + assert_equal 9, content_columns.length + assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved parent_title).sort, content_column_names.sort end def test_column_string_type_and_limit diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 0d9ee36..b844c7c 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -87,6 +87,8 @@ class Author < ActiveRecord::Base has_many :tags, :through => :posts # through has_many :through has_many :post_categories, :through => :posts, :source => :categories + has_one :essay, :primary_key => :name, :as => :writer + belongs_to :author_address, :dependent => :destroy belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress" diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index eb68153..2a65b03 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -84,6 +84,7 @@ class Client < Company belongs_to :firm_with_select, :class_name => "Firm", :foreign_key => "firm_id", :select => "id" belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of" belongs_to :firm_with_condition, :class_name => "Firm", :foreign_key => "client_of", :conditions => ["1 = ?", 1] + belongs_to :firm_with_primary_key, :class_name => "Firm", :primary_key => "name", :foreign_key => "firm_name" belongs_to :readonly_firm, :class_name => "Firm", :foreign_key => "firm_id", :readonly => true # Record destruction so we can test whether firm.clients.clear has diff --git a/activerecord/test/models/essay.rb b/activerecord/test/models/essay.rb new file mode 100644 index 0000000..6c28f5e --- /dev/null +++ b/activerecord/test/models/essay.rb @@ -0,0 +1,3 @@ +class Essay < ActiveRecord::Base + belongs_to :writer, :primary_key => :name, :polymorphic => true +end diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb index 1c990ac..4063785 100644 --- a/activerecord/test/models/reply.rb +++ b/activerecord/test/models/reply.rb @@ -4,12 +4,13 @@ class Reply < Topic named_scope :base belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true + belongs_to :topic_with_primary_key, :class_name => "Topic", :primary_key => "title", :foreign_key => "parent_title", :counter_cache => "replies_count" has_many :replies, :class_name => "SillyReply", :dependent => :destroy, :foreign_key => "parent_id" validate :errors_on_empty_content validate_on_create :title_is_wrong_create - attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read + attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read, :parent_title def validate errors.add("title", "Empty") unless attribute_present? "title" diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index 51012d2..201d96d 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -39,6 +39,7 @@ class Topic < ActiveRecord::Base named_scope :by_rejected_ids, lambda {{ :conditions => { :id => all(:conditions => {:approved => false}).map(&:id) } }} has_many :replies, :dependent => :destroy, :foreign_key => "parent_id" + has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title" serialize :content before_create :default_written_on diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 98e6d19..d080140 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -161,6 +161,12 @@ ActiveRecord::Schema.define do t.integer :course_id, :null => false end + create_table :essays, :force => true do |t| + t.string :name + t.string :writer_id + t.string :writer_type + end + create_table :events, :force => true do |t| t.string :title, :limit => 5 end @@ -418,6 +424,7 @@ ActiveRecord::Schema.define do t.boolean :approved, :default => true t.integer :replies_count, :default => 0 t.integer :parent_id + t.string :parent_title t.string :type end -- 1.6.4.2 From a147becfb86b689ab25e92edcfbb4bcc04108099 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 15 Jul 2009 15:44:45 +1200 Subject: [PATCH 146/779] Move from referencing the BlueCloth constant directly, to referencing Markdown. This supports alternative implementations of markdown such as rpeg-markdown or rdiscount, and later releases of bluecloth. There are some performance issues with earlier releases of bluecloth, and you should probably upgrade. In the event that you can't you can place the following code into an initializer: Markdown = BlueCloth --- actionpack/lib/action_view/helpers/text_helper.rb | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 48bf471..8463af9 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -271,8 +271,8 @@ module ActionView end # Returns the text with all the Markdown codes turned into HTML tags. - # This method requires BlueCloth[http://www.deveiate.org/projects/BlueCloth] - # to be available. + # This method requires BlueCloth[http://www.deveiate.org/projects/BlueCloth] or another + # Markdown library to be installed.. # # ==== Examples # markdown("We are using __Markdown__ now!") @@ -288,7 +288,7 @@ module ActionView # markdown('![The ROR logo](http://rubyonrails.com/images/rails.png "Ruby on Rails")') # # => '

The ROR logo

' def markdown(text) - text.blank? ? "" : BlueCloth.new(text).to_html + text.blank? ? "" : Markdown.new(text).to_html end # Returns +text+ transformed into HTML using simple formatting rules. -- 1.6.4.2 From c7bcbb983f84e306e2b43adb698060e060605de9 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 19 Jul 2009 17:27:45 +1200 Subject: [PATCH 147/779] Forgot to bump the railties versions --- railties/CHANGELOG | 4 ++++ railties/Rakefile | 10 +++++----- railties/lib/rails/version.rb | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/railties/CHANGELOG b/railties/CHANGELOG index e8e8434..b095fab 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,3 +1,7 @@ +*2.3.3 (July 12 2009) + +* Version bump + *2.3.2 [Final] (March 15, 2009)* * Allow metal to live in plugins #2045 [Matthew Rudy] diff --git a/railties/Rakefile b/railties/Rakefile index 792f345..9bdb337 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -311,11 +311,11 @@ spec = Gem::Specification.new do |s| EOF s.add_dependency('rake', '>= 0.8.3') - s.add_dependency('activesupport', '= 2.3.2' + PKG_BUILD) - s.add_dependency('activerecord', '= 2.3.2' + PKG_BUILD) - s.add_dependency('actionpack', '= 2.3.2' + PKG_BUILD) - s.add_dependency('actionmailer', '= 2.3.2' + PKG_BUILD) - s.add_dependency('activeresource', '= 2.3.2' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD) + s.add_dependency('activerecord', '= 2.3.3' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.3' + PKG_BUILD) + s.add_dependency('actionmailer', '= 2.3.3' + PKG_BUILD) + s.add_dependency('activeresource', '= 2.3.3' + PKG_BUILD) s.rdoc_options << '--exclude' << '.' s.has_rdoc = false diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index 99c7516..d3f81d8 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -2,7 +2,7 @@ module Rails module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 2 + TINY = 3 STRING = [MAJOR, MINOR, TINY].join('.') end -- 1.6.4.2 From 7a427a83ca4da92c70760007aaf313638a5d8374 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sun, 19 Jul 2009 14:13:28 +0900 Subject: [PATCH 148/779] Ruby 1.9.2 compat: Use File#expand_path for require path because "." will not be included in LOAD_PATH since Ruby 1.9.2 [#2921 state:resolved] Signed-off-by: Yehuda Katz --- railties/bin/about | 4 ++-- railties/bin/console | 2 +- railties/bin/dbconsole | 2 +- railties/bin/destroy | 2 +- railties/bin/generate | 2 +- railties/bin/performance/benchmarker | 2 +- railties/bin/performance/profiler | 2 +- railties/bin/plugin | 2 +- railties/bin/runner | 2 +- railties/bin/server | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/railties/bin/about b/railties/bin/about index ed8deb0..1eeb6eb 100755 --- a/railties/bin/about +++ b/railties/bin/about @@ -1,4 +1,4 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) $LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info" -require 'commands/about' \ No newline at end of file +require 'commands/about' diff --git a/railties/bin/console b/railties/bin/console index 498077a..235a1f2 100755 --- a/railties/bin/console +++ b/railties/bin/console @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/console' diff --git a/railties/bin/dbconsole b/railties/bin/dbconsole index caa60ce..83c8436 100755 --- a/railties/bin/dbconsole +++ b/railties/bin/dbconsole @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/dbconsole' diff --git a/railties/bin/destroy b/railties/bin/destroy index a4df765..88d295f 100755 --- a/railties/bin/destroy +++ b/railties/bin/destroy @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/destroy' diff --git a/railties/bin/generate b/railties/bin/generate index 173a9f1..62a8a4c 100755 --- a/railties/bin/generate +++ b/railties/bin/generate @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/generate' diff --git a/railties/bin/performance/benchmarker b/railties/bin/performance/benchmarker index c842d35..3bff809 100755 --- a/railties/bin/performance/benchmarker +++ b/railties/bin/performance/benchmarker @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' +require File.expand_path('../../../config/boot', __FILE__) require 'commands/performance/benchmarker' diff --git a/railties/bin/performance/profiler b/railties/bin/performance/profiler index d855ac8..0764057 100755 --- a/railties/bin/performance/profiler +++ b/railties/bin/performance/profiler @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' +require File.expand_path('../../../config/boot', __FILE__) require 'commands/performance/profiler' diff --git a/railties/bin/plugin b/railties/bin/plugin index 87cd207..b82201f 100755 --- a/railties/bin/plugin +++ b/railties/bin/plugin @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/plugin' diff --git a/railties/bin/runner b/railties/bin/runner index a4a7cb2..be4c5d4 100755 --- a/railties/bin/runner +++ b/railties/bin/runner @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/runner' diff --git a/railties/bin/server b/railties/bin/server index 3c67f39..b9fcb71 100755 --- a/railties/bin/server +++ b/railties/bin/server @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/server' -- 1.6.4.2 From be4d743645b47927b442ed2816ca0fa33421561e Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sun, 19 Jul 2009 14:36:44 +0900 Subject: [PATCH 149/779] Ruby 1.9.2 compat: name method was renamed to __name__ since MiniTest 1.4.x [#2922 state:resolved] Signed-off-by: Yehuda Katz --- activesupport/lib/active_support/test_case.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index 62fe7f5..bde74fa 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -17,7 +17,8 @@ module ActiveSupport class TestCase < ::Test::Unit::TestCase if defined? MiniTest Assertion = MiniTest::Assertion - alias_method :method_name, :name + alias_method :method_name, :name if method_defined? :name + alias_method :method_name, :__name__ if method_defined? :__name__ else # TODO: Figure out how to get the Rails::BacktraceFilter into minitest/unit if defined?(Rails) && ENV['BACKTRACE'].nil? -- 1.6.4.2 From 143c55d325388aa2384de8f137cfb61c09912208 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 22 Jul 2009 09:46:26 +1200 Subject: [PATCH 150/779] Memoize cookies so that updates to cookies are available in the current request. [#2733 state:resolved] Signed-off-by: Joshua Peek Conflicts: actionpack/test/controller/cookie_test.rb --- actionpack/lib/action_controller/cookies.rb | 2 +- actionpack/test/controller/cookie_test.rb | 6 ++ actionpack/test/fixtures/public/absolute/test.css | 23 ++++++++ actionpack/test/fixtures/public/absolute/test.js | 63 +++++++++++++++++++++ 4 files changed, 93 insertions(+), 1 deletions(-) create mode 100644 actionpack/test/fixtures/public/absolute/test.css create mode 100644 actionpack/test/fixtures/public/absolute/test.js diff --git a/actionpack/lib/action_controller/cookies.rb b/actionpack/lib/action_controller/cookies.rb index ca380e9..d480662 100644 --- a/actionpack/lib/action_controller/cookies.rb +++ b/actionpack/lib/action_controller/cookies.rb @@ -51,7 +51,7 @@ module ActionController #:nodoc: protected # Returns the cookie container, which operates as described above. def cookies - CookieJar.new(self) + @cookies ||= CookieJar.new(self) end end diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb index f7d97e1..ac47536 100644 --- a/actionpack/test/controller/cookie_test.rb +++ b/actionpack/test/controller/cookie_test.rb @@ -118,4 +118,10 @@ class CookieTest < ActionController::TestCase get :delete_cookie_with_path assert_equal ["user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"], @response.headers["Set-Cookie"] end + + def test_cookies_persist_throughout_request + get :authenticate + cookies = @controller.send(:cookies) + assert_equal 'david', cookies['user_name'] + end end diff --git a/actionpack/test/fixtures/public/absolute/test.css b/actionpack/test/fixtures/public/absolute/test.css new file mode 100644 index 0000000..6e09025 --- /dev/null +++ b/actionpack/test/fixtures/public/absolute/test.css @@ -0,0 +1,23 @@ +/* bank.css */ + +/* robber.css */ + +/* version.1.0.css */ + +/* bank.css */ + +/* bank.css */ + +/* robber.css */ + +/* version.1.0.css */ + +/* bank.css */ + +/* robber.css */ + +/* version.1.0.css */ + +/* robber.css */ + +/* version.1.0.css */ \ No newline at end of file diff --git a/actionpack/test/fixtures/public/absolute/test.js b/actionpack/test/fixtures/public/absolute/test.js new file mode 100644 index 0000000..4c4292a --- /dev/null +++ b/actionpack/test/fixtures/public/absolute/test.js @@ -0,0 +1,63 @@ +// prototype js + +// effects js + +// dragdrop js + +// controls js + +// prototype js + +// effects js + +// dragdrop js + +// controls js + +// application js + +// bank js + +// robber js + +// version.1.0 js + +// application js + +// bank js + +// prototype js + +// effects js + +// dragdrop js + +// controls js + +// prototype js + +// effects js + +// dragdrop js + +// controls js + +// application js + +// bank js + +// robber js + +// version.1.0 js + +// application js + +// bank js + +// robber js + +// version.1.0 js + +// robber js + +// version.1.0 js \ No newline at end of file -- 1.6.4.2 From ead5d88bf178b8a32d151747a42df2aafa57eeaa Mon Sep 17 00:00:00 2001 From: Sebastian Delmont Date: Mon, 20 Jul 2009 17:53:23 -0400 Subject: [PATCH 151/779] Fix filter_parameter_logging of non-hash values within array params Signed-off-by: Michael Koziarski [#2927 state:committed] --- actionpack/lib/action_controller/base.rb | 7 ++++++- actionpack/test/controller/filter_params_test.rb | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 3c89fc8..0801bd6 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -493,7 +493,12 @@ module ActionController #:nodoc: filtered_parameters[key] = filter_parameters(value) elsif value.is_a?(Array) filtered_parameters[key] = value.collect do |item| - filter_parameters(item) + case item + when Hash, Array + filter_parameters(item) + else + item + end end elsif block_given? key = key.dup diff --git a/actionpack/test/controller/filter_params_test.rb b/actionpack/test/controller/filter_params_test.rb index 0f48088..6994aae 100644 --- a/actionpack/test/controller/filter_params_test.rb +++ b/actionpack/test/controller/filter_params_test.rb @@ -24,7 +24,8 @@ class FilterParamTest < Test::Unit::TestCase [{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'], [{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'], [{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana'], - [{'baz'=>[{'foo'=>'baz'}]}, {'baz'=>[{'foo'=>'[FILTERED]'}]}, %w(foo)]] + [{'baz'=>[{'foo'=>'baz'}]}, {'baz'=>[{'foo'=>'[FILTERED]'}]}, %w(foo)], + [{'baz'=>[{'foo'=>'baz'}, 1, 2, 3]}, {'baz'=>[{'foo'=>'[FILTERED]'}, 1, 2, 3]}, %w(foo)]] test_hashes.each do |before_filter, after_filter, filter_words| FilterParamController.filter_parameter_logging(*filter_words) -- 1.6.4.2 From 60122e81a3504ed171a761573ed16cb034c79c13 Mon Sep 17 00:00:00 2001 From: Luke Melia Date: Sat, 30 May 2009 09:34:57 -0500 Subject: [PATCH 152/779] Avoid loading the ActiveRecord::SessionStore class on initialization if it is not in use [#2737 state:resolved] Signed-off-by: Joshua Peek --- railties/lib/initializer.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 5f5e557..196bd93 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -442,7 +442,7 @@ Run `rake gems:install` to install the missing gems. def initialize_database_middleware if configuration.frameworks.include?(:active_record) if configuration.frameworks.include?(:action_controller) && - ActionController::Base.session_store == ActiveRecord::SessionStore + ActionController::Base.session_store.name == 'ActiveRecord::SessionStore' configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement configuration.middleware.insert_before :"ActiveRecord::SessionStore", ActiveRecord::QueryCache else -- 1.6.4.2 From 523f3ba8daf4968267eb4597bc88359a6337cf90 Mon Sep 17 00:00:00 2001 From: Ross Kaffenburger and Bryan Helmkamp Date: Wed, 4 Mar 2009 16:05:15 -0500 Subject: [PATCH 153/779] Don't check authenticity tokens for any AJAX requests --- actionpack/CHANGELOG | 2 ++ .../request_forgery_protection.rb | 3 ++- .../controller/request_forgery_protection_test.rb | 11 ++++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 5f6fec6..13ed609 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -7,6 +7,8 @@ * Fixed that redirection would just log the options, not the final url (which lead to "Redirected to #") [DHH] +* Don't check authenticity tokens for any AJAX requests [Ross Kaffenberger/Bryan Helmkamp] + * Added ability to pass in :public => true to fresh_when, stale?, and expires_in to make the request proxy cachable #2095 [Gregg Pollack] * Fixed that passing a custom form builder would be forwarded to nested fields_for calls #2023 [Eloy Duran/Nate Wiger] diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/request_forgery_protection.rb index f3e6288..3067122 100644 --- a/actionpack/lib/action_controller/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/request_forgery_protection.rb @@ -81,12 +81,13 @@ module ActionController #:nodoc: # Returns true or false if a request is verified. Checks: # - # * is the format restricted? By default, only HTML and AJAX requests are checked. + # * is the format restricted? By default, only HTML requests are checked. # * is it a GET request? Gets should be safe and idempotent # * Does the form_authenticity_token match the given token value from the params? def verified_request? !protect_against_forgery? || request.method == :get || + request.xhr? || !verifiable_request_format? || form_authenticity_token == params[request_forgery_protection_token] end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 835e73e..83925ed 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -151,14 +151,10 @@ module RequestForgeryProtectionTests delete :index, :format => 'xml' end end - + def test_should_allow_xhr_post_without_token assert_nothing_raised { xhr :post, :index } end - def test_should_not_allow_xhr_post_with_html_without_token - @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s - assert_raise(ActionController::InvalidAuthenticityToken) { xhr :post, :index } - end def test_should_allow_xhr_put_without_token assert_nothing_raised { xhr :put, :index } @@ -168,6 +164,11 @@ module RequestForgeryProtectionTests assert_nothing_raised { xhr :delete, :index } end + def test_should_allow_xhr_post_with_encoded_form_content_type_without_token + @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s + assert_nothing_raised { xhr :post, :index } + end + def test_should_allow_post_with_token post :index, :authenticity_token => @token assert_response :success -- 1.6.4.2 From 17f336e2f00f419a41eb7effb817bd7ad3e84f0d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 29 Jul 2009 16:53:49 -0700 Subject: [PATCH 154/779] Revert "Methods invoked within named scope Procs should respect the scope stack. [#1267 state:resolved]" This reverts commit 6a13376525f34a00e013fc3a6022838329dfe856. Conflicts: activerecord/test/cases/named_scope_test.rb --- activerecord/lib/active_record/named_scope.rb | 7 +------ activerecord/test/cases/named_scope_test.rb | 4 ---- activerecord/test/models/topic.rb | 2 -- 3 files changed, 1 insertions(+), 12 deletions(-) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 3df7089..0b77224 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -89,12 +89,7 @@ module ActiveRecord when Hash options when Proc - case parent_scope - when Scope - with_scope(:find => parent_scope.proxy_options) { options.call(*args) } - else - options.call(*args) - end + options.call(*args) end, &block) end (class << self; self end).instance_eval do diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index ae6a54a..0d63c78 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -319,10 +319,6 @@ class NamedScopeTest < ActiveRecord::TestCase assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).all.uniq end - def test_methods_invoked_within_scopes_should_respect_scope - assert_equal [], Topic.approved.by_rejected_ids.proxy_options[:conditions][:id] - end - def test_named_scopes_batch_finders assert_equal 3, Topic.approved.count diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index 201d96d..9594dc3 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -35,8 +35,6 @@ class Topic < ActiveRecord::Base end named_scope :named_extension, :extend => NamedExtension named_scope :multiple_extensions, :extend => [MultipleExtensionTwo, MultipleExtensionOne] - - named_scope :by_rejected_ids, lambda {{ :conditions => { :id => all(:conditions => {:approved => false}).map(&:id) } }} has_many :replies, :dependent => :destroy, :foreign_key => "parent_id" has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title" -- 1.6.4.2 From d37ac7958fc88fdbf37a8948102f6b4e45c530b3 Mon Sep 17 00:00:00 2001 From: Hongli Lai (Phusion) Date: Fri, 31 Jul 2009 15:35:53 +0200 Subject: [PATCH 155/779] Make the new code reloading behavior work with multithreaded environments such as Mongrel. [#2948 state:incomplete] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/reloader.rb | 14 ++++++-- actionpack/test/controller/reloader_test.rb | 44 ++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_controller/reloader.rb b/actionpack/lib/action_controller/reloader.rb index 459dece..cfc6bfc 100644 --- a/actionpack/lib/action_controller/reloader.rb +++ b/actionpack/lib/action_controller/reloader.rb @@ -1,14 +1,20 @@ +require 'thread' + module ActionController class Reloader + @@lock = Mutex.new + class BodyWrapper - def initialize(body) + def initialize(body, lock) @body = body + @lock = lock end def close @body.close if @body.respond_to?(:close) ensure Dispatcher.cleanup_application + @lock.unlock end def method_missing(*args, &block) @@ -20,11 +26,13 @@ module ActionController end end - def initialize(app) + def initialize(app, lock = @@lock) @app = app + @lock = lock end def call(env) + @lock.lock Dispatcher.reload_application status, headers, body = @app.call(env) # We do not want to call 'cleanup_application' in an ensure block @@ -39,7 +47,7 @@ module ActionController # completely finished. So we wrap the body in a BodyWrapper class so that # when the Rack handler calls #close during the end of the request, we get to # run our cleanup code. - [status, headers, BodyWrapper.new(body)] + [status, headers, BodyWrapper.new(body, @lock)] end end end diff --git a/actionpack/test/controller/reloader_test.rb b/actionpack/test/controller/reloader_test.rb index 6f1f3c8..045351c 100644 --- a/actionpack/test/controller/reloader_test.rb +++ b/actionpack/test/controller/reloader_test.rb @@ -22,9 +22,27 @@ class ReloaderTests < ActiveSupport::TestCase end end + class MyLock + def lock + @locked = true + end + + def unlock + @locked = false + end + + def locked? + @locked + end + end + + def setup + @lock = Mutex.new + end + def setup_and_return_body(app = lambda { }) Dispatcher.expects(:reload_application) - reloader = Reloader.new(app) + reloader = Reloader.new(app, @lock) headers, status, body = reloader.call({ }) body end @@ -33,10 +51,32 @@ class ReloaderTests < ActiveSupport::TestCase Dispatcher.expects(:reload_application) reloader = Reloader.new(lambda { [200, { "Content-Type" => "text/html" }, [""]] - }) + }, @lock) reloader.call({ }) end + def test_it_locks_before_calling_app + lock = MyLock.new + Dispatcher.expects(:reload_application) + reloader = Reloader.new(lambda { + [200, { "Content-Type" => "text/html" }, [""]] + }, lock) + assert !lock.locked? + reloader.call({ }) + assert lock.locked? + end + + def it_unlocks_upon_calling_close_on_body + lock = MyLock.new + Dispatcher.expects(:reload_application) + reloader = Reloader.new(lambda { + [200, { "Content-Type" => "text/html" }, [""]] + }, lock) + headers, status, body = reloader.call({ }) + body.close + assert !lock.locked? + end + def test_returned_body_object_always_responds_to_close body = setup_and_return_body(lambda { [200, { "Content-Type" => "text/html" }, [""]] -- 1.6.4.2 From 32cfd4c2f8cebf3dd331b2f830a268dbf637adf3 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 1 Aug 2009 18:21:11 -0700 Subject: [PATCH 156/779] SQLite: deprecate the 'dbfile' option in favor of 'database.' --- activerecord/CHANGELOG | 5 +++++ .../connection_adapters/sqlite_adapter.rb | 4 ++++ 2 files changed, 9 insertions(+), 0 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 5593a22..735ba0b 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.4 (pending)* + +* SQLite: deprecate the 'dbfile' option in favor of 'database.' #2363 [Paul Hinze, Jeremy Kemper] + + *2.3.3 (July 12, 2009)* * Added :primary_key option to belongs_to associations. #765 [Szymon Nowak, Philip Hallstrom, Noel Rocha] diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 69f7975..250318f 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -27,6 +27,10 @@ module ActiveRecord private def parse_sqlite_config!(config) + if config.include?(:dbfile) + ActiveSupport::Deprecation.warn "Please update config/database.yml to use 'database' instead of 'dbfile'" + end + config[:database] ||= config[:dbfile] # Require database. unless config[:database] -- 1.6.4.2 From 21029451d7423b51be7afb96e1b6647b87c5be5c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 1 Aug 2009 19:50:54 -0700 Subject: [PATCH 157/779] Fix errant require --- activesupport/Rakefile | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/activesupport/Rakefile b/activesupport/Rakefile index ccbab52..d5ece39 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -87,9 +87,6 @@ task :release => [ :package ] do rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages) end - -require 'lib/active_support/values/time_zone' - namespace :tzinfo do desc "Update bundled tzinfo gem. Only copies the subset of classes and definitions required to support Rails time zone features." task :update => ['tzinfo:copy_classes', 'tzinfo:copy_definitions'] do @@ -118,6 +115,8 @@ namespace :tzinfo do end task :copy_definitions => :unpack_gem do + $:.unshift "#{File.dirname(__FILE__)}/lib" + require 'active_support/values/time_zone' definitions_path = "#{destination_path}/tzinfo/definitions/" mkdir_p definitions_path ActiveSupport::TimeZone::MAPPING.values.each do |zone| -- 1.6.4.2 From 7244425b93ded0789b73681533df84ffcd562852 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 1 Aug 2009 18:34:41 -0700 Subject: [PATCH 158/779] Extract String#bytesize shim --- .../lib/active_support/core_ext/string.rb | 1 + .../lib/active_support/core_ext/string/bytesize.rb | 5 +++++ activesupport/test/core_ext/string_ext_test.rb | 7 +++++++ 3 files changed, 13 insertions(+), 0 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/string/bytesize.rb diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb index 16c544a..2600f84 100644 --- a/activesupport/lib/active_support/core_ext/string.rb +++ b/activesupport/lib/active_support/core_ext/string.rb @@ -1,6 +1,7 @@ # encoding: utf-8 require 'active_support/core_ext/string/inflections' +require 'active_support/core_ext/string/bytesize' require 'active_support/core_ext/string/conversions' require 'active_support/core_ext/string/access' require 'active_support/core_ext/string/starts_ends_with' diff --git a/activesupport/lib/active_support/core_ext/string/bytesize.rb b/activesupport/lib/active_support/core_ext/string/bytesize.rb new file mode 100644 index 0000000..ed051b9 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/string/bytesize.rb @@ -0,0 +1,5 @@ +unless '1.9'.respond_to?(:bytesize) + class String + alias :bytesize :size + end +end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 7d51e81..e57008b 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -274,3 +274,10 @@ class CoreExtStringMultibyteTest < ActiveSupport::TestCase end end end + +class StringBytesizeTest < Test::Unit::TestCase + def test_bytesize + assert_respond_to 'foo', :bytesize + assert_equal 3, 'foo'.bytesize + end +end -- 1.6.4.2 From dc559f274fbd1ba180a785e741b9b2d7a7d09ae7 Mon Sep 17 00:00:00 2001 From: Sava Chankov Date: Sat, 1 Aug 2009 19:38:05 -0700 Subject: [PATCH 159/779] Ruby 1.9: fix Content-Length for multibyte send_data streaming [#2661 state:resolved] Signed-off-by: Jeremy Kemper --- actionpack/CHANGELOG | 5 +++++ actionpack/lib/action_controller/streaming.rb | 4 +++- actionpack/test/controller/send_file_test.rb | 9 +++++++++ 3 files changed, 17 insertions(+), 1 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 13ed609..da3749f 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.4 (pending)* + +* Ruby 1.9: fix Content-Length for multibyte send_data streaming. #2661 [Sava Chankov] + + *2.3.3 (July 12, 2009)* * Fixed that TestResponse.cookies was returning cookies unescaped #1867 [Doug McInnes] diff --git a/actionpack/lib/action_controller/streaming.rb b/actionpack/lib/action_controller/streaming.rb index 8a9fbfc..372470f 100644 --- a/actionpack/lib/action_controller/streaming.rb +++ b/actionpack/lib/action_controller/streaming.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/bytesize' + module ActionController #:nodoc: # Methods for sending arbitrary data and for streaming files to the browser, # instead of rendering. @@ -137,7 +139,7 @@ module ActionController #:nodoc: # instead. See ActionController::Base#render for more information. def send_data(data, options = {}) #:doc: logger.info "Sending data #{options[:filename]}" if logger - send_file_headers! options.merge(:length => data.size) + send_file_headers! options.merge(:length => data.bytesize) @performed_render = false render :status => options[:status], :text => data end diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 26c7f9b..ae588ad 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -1,3 +1,4 @@ +# encoding: utf-8 require 'abstract_unit' module TestFileUtils @@ -15,6 +16,7 @@ class SendFileController < ActionController::Base def file() send_file(file_path, options) end def data() send_data(file_data, options) end + def multibyte_text_data() send_data("Кирилица\n祝您好運", options) end def rescue_action(e) raise end end @@ -158,4 +160,11 @@ class SendFileTest < ActionController::TestCase assert_equal ActionController::Base::DEFAULT_RENDER_STATUS_CODE, @response.status end end + + def test_send_data_content_length_header + @controller.headers = {} + @controller.options = { :type => :text, :filename => 'file_with_utf8_text' } + process('multibyte_text_data') + assert_equal '29', @controller.headers['Content-Length'] + end end -- 1.6.4.2 From f09ceb55c0eaa8f81d5cb426501a32f86f96b23c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 1 Aug 2009 20:07:28 -0700 Subject: [PATCH 160/779] Ruby 1.9: fix encoding for test_file_stream --- actionpack/test/controller/send_file_test.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index ae588ad..f61bbc6 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -4,7 +4,7 @@ require 'abstract_unit' module TestFileUtils def file_name() File.basename(__FILE__) end def file_path() File.expand_path(__FILE__) end - def file_data() File.open(file_path, 'rb') { |f| f.read } end + def file_data() @data ||= File.open(file_path, 'rb') { |f| f.read } end end class SendFileController < ActionController::Base @@ -51,6 +51,7 @@ class SendFileTest < ActionController::TestCase require 'stringio' output = StringIO.new output.binmode + output.string.force_encoding(file_data.encoding) if output.string.respond_to?(:force_encoding) assert_nothing_raised { response.body.call(response, output) } assert_equal file_data, output.string end -- 1.6.4.2 From 6cbcfffeb118ecbb7c1930c74de7b8b6a49dfacd Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 1 Aug 2009 20:18:22 -0700 Subject: [PATCH 161/779] Ruby 1.9: fix reloader tests using lambdas with no env arg --- actionpack/test/controller/reloader_test.rb | 20 ++++++++++---------- 1 files changed, 10 insertions(+), 10 deletions(-) diff --git a/actionpack/test/controller/reloader_test.rb b/actionpack/test/controller/reloader_test.rb index 045351c..5aa5678 100644 --- a/actionpack/test/controller/reloader_test.rb +++ b/actionpack/test/controller/reloader_test.rb @@ -40,7 +40,7 @@ class ReloaderTests < ActiveSupport::TestCase @lock = Mutex.new end - def setup_and_return_body(app = lambda { }) + def setup_and_return_body(app = lambda { |env| }) Dispatcher.expects(:reload_application) reloader = Reloader.new(app, @lock) headers, status, body = reloader.call({ }) @@ -49,7 +49,7 @@ class ReloaderTests < ActiveSupport::TestCase def test_it_reloads_the_application_before_the_request Dispatcher.expects(:reload_application) - reloader = Reloader.new(lambda { + reloader = Reloader.new(lambda { |env| [200, { "Content-Type" => "text/html" }, [""]] }, @lock) reloader.call({ }) @@ -58,7 +58,7 @@ class ReloaderTests < ActiveSupport::TestCase def test_it_locks_before_calling_app lock = MyLock.new Dispatcher.expects(:reload_application) - reloader = Reloader.new(lambda { + reloader = Reloader.new(lambda { |env| [200, { "Content-Type" => "text/html" }, [""]] }, lock) assert !lock.locked? @@ -69,7 +69,7 @@ class ReloaderTests < ActiveSupport::TestCase def it_unlocks_upon_calling_close_on_body lock = MyLock.new Dispatcher.expects(:reload_application) - reloader = Reloader.new(lambda { + reloader = Reloader.new(lambda { |env| [200, { "Content-Type" => "text/html" }, [""]] }, lock) headers, status, body = reloader.call({ }) @@ -78,14 +78,14 @@ class ReloaderTests < ActiveSupport::TestCase end def test_returned_body_object_always_responds_to_close - body = setup_and_return_body(lambda { + body = setup_and_return_body(lambda { |env| [200, { "Content-Type" => "text/html" }, [""]] }) assert body.respond_to?(:close) end def test_returned_body_object_behaves_like_underlying_object - body = setup_and_return_body(lambda { + body = setup_and_return_body(lambda { |env| b = MyBody.new b << "hello" b << "world" @@ -100,7 +100,7 @@ class ReloaderTests < ActiveSupport::TestCase def test_it_calls_close_on_underlying_object_when_close_is_called_on_body close_called = false - body = setup_and_return_body(lambda { + body = setup_and_return_body(lambda { |env| b = MyBody.new do close_called = true end @@ -111,7 +111,7 @@ class ReloaderTests < ActiveSupport::TestCase end def test_returned_body_object_responds_to_all_methods_supported_by_underlying_object - body = setup_and_return_body(lambda { + body = setup_and_return_body(lambda { |env| [200, { "Content-Type" => "text/html" }, MyBody.new] }) assert body.respond_to?(:size) @@ -122,14 +122,14 @@ class ReloaderTests < ActiveSupport::TestCase def test_it_doesnt_clean_up_the_application_after_call Dispatcher.expects(:cleanup_application).never - body = setup_and_return_body(lambda { + body = setup_and_return_body(lambda { |env| [200, { "Content-Type" => "text/html" }, MyBody.new] }) end def test_it_cleans_up_the_application_when_close_is_called_on_body Dispatcher.expects(:cleanup_application) - body = setup_and_return_body(lambda { + body = setup_and_return_body(lambda { |env| [200, { "Content-Type" => "text/html" }, MyBody.new] }) body.close -- 1.6.4.2 From bf00de03dee5fba0b53f0fc1bb19464422550aa9 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 8 Jul 2009 18:49:40 +0200 Subject: [PATCH 162/779] Stop messing with supposedly-deprecated interpolation placeholders when no interpolation values have been passed. [#2885 state:committed] Signed-off-by: Jeremy Kemper --- .../i18n_interpolation_deprecation.rb | 2 +- activerecord/test/cases/validations_i18n_test.rb | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb b/activerecord/lib/active_record/i18n_interpolation_deprecation.rb index cd634e1..ccb0cac 100644 --- a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb +++ b/activerecord/lib/active_record/i18n_interpolation_deprecation.rb @@ -10,7 +10,7 @@ module I18n protected def interpolate_with_deprecated_syntax(locale, string, values = {}) - return string unless string.is_a?(String) + return string unless string.is_a?(String) && !values.empty? string = string.gsub(/%d|%s/) do |s| instead = DEPRECATED_INTERPOLATORS[s] diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 6698234..1585554 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -53,7 +53,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase def test_percent_s_interpolation_syntax_in_error_messages_is_deprecated assert_deprecated('using %s in messages') do - I18n.t :does_not_exist, :default => "%s interpolation syntax is deprected", :value => 'this' + I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this' end end @@ -70,6 +70,12 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase end end + def test_percent_s_interpolation_syntax_not_changed_when_no_values_were_passed + assert_not_deprecated do + I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected" + end + end + # ActiveRecord::Errors def test_errors_generate_message_translates_custom_model_attribute_key -- 1.6.4.2 From 95db8aaa5f395b77e4a54a6cc1bed741251dc81f Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Mon, 27 Apr 2009 16:50:20 +1200 Subject: [PATCH 163/779] Run the view tests too --- actionpack/Rakefile | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 17f6f1a..1182958 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -29,7 +29,7 @@ Rake::TestTask.new(:test_action_pack) do |t| # 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/[cft]*/**/*_test.rb" ).sort + t.test_files = Dir.glob( "test/[cftv]*/**/*_test.rb" ).sort t.verbose = true #t.warning = true -- 1.6.4.2 From 5a0e2959117e29e6836b9f611d6ba1d3260702b7 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 5 Aug 2009 19:40:11 +0100 Subject: [PATCH 164/779] Make app template git adapter sync back output immediately by using system() instead of backticks [#2047 state:resolved] Signed-off-by: Pratik Naik --- .../generators/applications/app/scm/git.rb | 10 ++++++---- 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/railties/lib/rails_generator/generators/applications/app/scm/git.rb b/railties/lib/rails_generator/generators/applications/app/scm/git.rb index 445de6a..a53494d 100644 --- a/railties/lib/rails_generator/generators/applications/app/scm/git.rb +++ b/railties/lib/rails_generator/generators/applications/app/scm/git.rb @@ -1,16 +1,18 @@ +STDOUT.sync = true + module Rails class Git < Scm def self.clone(repos, branch=nil) - `git clone #{repos}` + system "git clone #{repos}" if branch - `cd #{repos.split('/').last}/` - `git checkout #{branch}` + system "cd #{repos.split('/').last}/" + system "git checkout #{branch}" end end def self.run(command) - `git #{command}` + system "git #{command}" end end end \ No newline at end of file -- 1.6.4.2 From 55501b9f6ab46d45db04a81956579402511ad092 Mon Sep 17 00:00:00 2001 From: rick Date: Wed, 5 Aug 2009 16:27:02 -0700 Subject: [PATCH 165/779] move the serialized AR record logic to #as_json. Leave ActiveRecord::Base#to_json to use the same Object#to_json implementation, but keep it around for documentation purposes. --- .../active_record/serializers/json_serializer.rb | 8 +++++--- 1 files changed, 5 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/serializers/json_serializer.rb b/activerecord/lib/active_record/serializers/json_serializer.rb index 085d4f1..a20dfbc 100644 --- a/activerecord/lib/active_record/serializers/json_serializer.rb +++ b/activerecord/lib/active_record/serializers/json_serializer.rb @@ -74,13 +74,15 @@ module ActiveRecord #:nodoc: # {"comments": [{"body": "Don't think too hard"}], # "title": "So I was thinking"}]} def to_json(options = {}) + super + end + + def as_json(options = nil) #:nodoc: hash = Serializer.new(self, options).serializable_record hash = { self.class.model_name.element => hash } if include_root_in_json - ActiveSupport::JSON.encode(hash) + hash end - def as_json(options = nil) self end #:nodoc: - def from_json(json) self.attributes = ActiveSupport::JSON.decode(json) self -- 1.6.4.2 From 8dab61d146f26c18acdef1fcc57b01612f96440c Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 6 Aug 2009 08:34:23 +0900 Subject: [PATCH 166/779] Ruby 1.9.2 compat: Array#* uses to_str instead of to_s to join values since Ruby 1.9.2 [#2959 state:committed] Signed-off-by: Jeremy Kemper --- .../abstract/schema_definitions.rb | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 24c734c..f346e3e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -277,7 +277,6 @@ module ActiveRecord add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key column_sql end - alias to_s :to_sql private @@ -508,7 +507,7 @@ module ActiveRecord # concatenated together. This string can then be prepended and appended to # to generate the final SQL to create the table. def to_sql - @columns * ', ' + @columns.map(&:to_sql) * ', ' end private -- 1.6.4.2 From e1d27eedceea46531d11c03c1e1e61ba15a5fdf2 Mon Sep 17 00:00:00 2001 From: Brendan Schwartz Date: Wed, 25 Mar 2009 15:14:52 -0400 Subject: [PATCH 167/779] Ruby 1.9 compat: fix for SSL in Active Resource [#1272 state:committed] Signed-off-by: Jeremy Kemper --- activeresource/lib/active_resource/connection.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb index 85103b5..943c9ef 100644 --- a/activeresource/lib/active_resource/connection.rb +++ b/activeresource/lib/active_resource/connection.rb @@ -188,7 +188,7 @@ module ActiveResource def http http = Net::HTTP.new(@site.host, @site.port) http.use_ssl = @site.is_a?(URI::HTTPS) - http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl + http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl? http.read_timeout = @timeout if @timeout # If timeout is not set, the default Net::HTTP timeout (60s) is used. http end -- 1.6.4.2 From f73d34c131c1e371c76c5a146aac2c2bffbf96e5 Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 7 Aug 2009 21:37:21 -0400 Subject: [PATCH 168/779] Default sent_on time to now in ActionMailer Signed-off-by: Michael Koziarski [#2607 state:committed] --- actionmailer/lib/action_mailer/base.rb | 1 + actionmailer/test/mail_service_test.rb | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 000c05d..84997de 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -543,6 +543,7 @@ module ActionMailer #:nodoc: @headers ||= {} @body ||= {} @mime_version = @@default_mime_version.dup if @@default_mime_version + @sent_on ||= Time.now end def render_message(method_name, body) diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 277a913..3244aad 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -18,7 +18,6 @@ class TestMailer < ActionMailer::Base @recipients = recipient @subject = "[Signed up] Welcome #{recipient}" @from = "system@loudthinking.com" - @sent_on = Time.local(2004, 12, 12) @body["recipient"] = recipient end @@ -356,12 +355,14 @@ class ActionMailerTest < Test::Unit::TestCase end def test_signed_up + Time.stubs(:now => Time.now) + expected = new_mail expected.to = @recipient expected.subject = "[Signed up] Welcome #{@recipient}" expected.body = "Hello there, \n\nMr. #{@recipient}" expected.from = "system@loudthinking.com" - expected.date = Time.local(2004, 12, 12) + expected.date = Time.now created = nil assert_nothing_raised { created = TestMailer.create_signed_up(@recipient) } -- 1.6.4.2 From 1c6c216d911cc55ef7a338d2aab5c827ca6f3468 Mon Sep 17 00:00:00 2001 From: Mike Breen Date: Mon, 20 Jul 2009 16:48:08 -0400 Subject: [PATCH 169/779] Add option to routes task to target a specific controller with CONTROLLER=x. Signed-off-by: Michael Koziarski [#2928 state:committed] --- railties/lib/tasks/routes.rake | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/railties/lib/tasks/routes.rake b/railties/lib/tasks/routes.rake index 39b7139..abbf325 100644 --- a/railties/lib/tasks/routes.rake +++ b/railties/lib/tasks/routes.rake @@ -1,6 +1,7 @@ -desc 'Print out all defined routes in match order, with names.' +desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.' task :routes => :environment do - routes = ActionController::Routing::Routes.routes.collect do |route| + all_routes = ENV['CONTROLLER'] ? ActionController::Routing::Routes.routes.select { |route| route.defaults[:controller] == ENV['CONTROLLER'] } : ActionController::Routing::Routes.routes + routes = all_routes.collect do |route| name = ActionController::Routing::Routes.named_routes.routes.index(route).to_s verb = route.conditions[:method].to_s.upcase segs = route.segments.inject("") { |str,s| str << s.to_s } @@ -14,4 +15,4 @@ task :routes => :environment do routes.each do |r| puts "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:segs].ljust(segs_width)} #{r[:reqs]}" end -end \ No newline at end of file +end -- 1.6.4.2 From 9aaeb18781f329bf04c3174898bb8f32581a515e Mon Sep 17 00:00:00 2001 From: Josh Sharpe Date: Fri, 7 Aug 2009 21:12:27 -0400 Subject: [PATCH 170/779] Tidy up the AR tests, removing duplicates and making tests clearer / more focussed. Signed-off-by: Michael Koziarski [#2774 state:committed] --- activerecord/test/cases/finder_test.rb | 57 ++++++------------------------- 1 files changed, 11 insertions(+), 46 deletions(-) diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 25e339f..c157ceb 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -156,10 +156,8 @@ class FinderTest < ActiveRecord::TestCase end def test_find_all_with_limit - entrants = Entrant.find(:all, :order => "id ASC", :limit => 2) - - assert_equal(2, entrants.size) - assert_equal(entrants(:first).name, entrants.first.name) + assert_equal(2, Entrant.find(:all, :limit => 2).size) + assert_equal(0, Entrant.find(:all, :limit => 0).size) end def test_find_all_with_prepared_limit_and_offset @@ -168,22 +166,23 @@ class FinderTest < ActiveRecord::TestCase assert_equal(2, entrants.size) assert_equal(entrants(:second).name, entrants.first.name) + assert_equal 3, Entrant.count entrants = Entrant.find(:all, :order => "id ASC", :limit => 2, :offset => 2) assert_equal(1, entrants.size) assert_equal(entrants(:third).name, entrants.first.name) end - def test_find_all_with_limit_and_offset_and_multiple_orderings - developers = Developer.find(:all, :order => "salary ASC, id DESC", :limit => 3, :offset => 1) - assert_equal ["David", "fixture_10", "fixture_9"], developers.collect {|d| d.name} - end + def test_find_all_with_limit_and_offset_and_multiple_order_clauses + first_three_posts = Post.find :all, :order => 'author_id, id', :limit => 3, :offset => 0 + second_three_posts = Post.find :all, :order => ' author_id,id ', :limit => 3, :offset => 3 + last_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 6 - def test_find_with_limit_and_condition - developers = Developer.find(:all, :order => "id DESC", :conditions => "salary = 100000", :limit => 3, :offset =>7) - assert_equal(1, developers.size) - assert_equal("fixture_3", developers.first.name) + assert_equal [[0,3],[1,1],[1,2]], first_three_posts.map { |p| [p.author_id, p.id] } + assert_equal [[1,4],[1,5],[1,6]], second_three_posts.map { |p| [p.author_id, p.id] } + assert_equal [[2,7]], last_posts.map { |p| [p.author_id, p.id] } end + def test_find_with_group developers = Developer.find(:all, :group => "salary", :select => "salary") assert_equal 4, developers.size @@ -941,40 +940,6 @@ class FinderTest < ActiveRecord::TestCase assert_raise(ArgumentError) { Topic.find_by_title 'No Title', :join => "It should be `joins'" } end - def test_find_all_with_limit - first_five_developers = Developer.find :all, :order => 'id ASC', :limit => 5 - assert_equal 5, first_five_developers.length - assert_equal 'David', first_five_developers.first.name - assert_equal 'fixture_5', first_five_developers.last.name - - no_developers = Developer.find :all, :order => 'id ASC', :limit => 0 - assert_equal 0, no_developers.length - end - - def test_find_all_with_limit_and_offset - first_three_developers = Developer.find :all, :order => 'id ASC', :limit => 3, :offset => 0 - second_three_developers = Developer.find :all, :order => 'id ASC', :limit => 3, :offset => 3 - last_two_developers = Developer.find :all, :order => 'id ASC', :limit => 2, :offset => 8 - - assert_equal 3, first_three_developers.length - assert_equal 3, second_three_developers.length - assert_equal 2, last_two_developers.length - - assert_equal 'David', first_three_developers.first.name - assert_equal 'fixture_4', second_three_developers.first.name - assert_equal 'fixture_9', last_two_developers.first.name - end - - def test_find_all_with_limit_and_offset_and_multiple_order_clauses - first_three_posts = Post.find :all, :order => 'author_id, id', :limit => 3, :offset => 0 - second_three_posts = Post.find :all, :order => ' author_id,id ', :limit => 3, :offset => 3 - last_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 6 - - assert_equal [[0,3],[1,1],[1,2]], first_three_posts.map { |p| [p.author_id, p.id] } - assert_equal [[1,4],[1,5],[1,6]], second_three_posts.map { |p| [p.author_id, p.id] } - assert_equal [[2,7]], last_posts.map { |p| [p.author_id, p.id] } - end - def test_find_all_with_join developers_on_project_one = Developer.find( :all, -- 1.6.4.2 From 6843fb9265c6eb48f00aceb181e6801a1c072478 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 23 Apr 2009 10:26:52 -0300 Subject: [PATCH 171/779] Fix models load order to be able to run unit tests. [#2550 state:committed] Signed-off-by: Jeremy Kemper --- .../associations/eager_load_nested_include_test.rb | 10 +++++----- activerecord/test/cases/base_test.rb | 6 +++--- activerecord/test/cases/finder_test.rb | 8 ++++---- activerecord/test/cases/method_scoping_test.rb | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb index 1b2e0fc..677226e 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -1,6 +1,6 @@ require 'cases/helper' -require 'models/author' require 'models/post' +require 'models/author' require 'models/comment' require 'models/category' require 'models/categorization' @@ -66,13 +66,13 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase def setup generate_test_object_graphs end - + def teardown - [Circle, Square, Triangle, PaintColor, PaintTexture, + [Circle, Square, Triangle, PaintColor, PaintTexture, ShapeExpression, NonPolyOne, NonPolyTwo].each do |c| c.delete_all end - + end @@ -127,4 +127,4 @@ class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase Author.all :include => includes, :conditions => {:authors => {:name => @davey_mcdave.name}}, :order => 'categories.name' end end -end \ No newline at end of file +end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 604271e..e57e46f 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1,4 +1,5 @@ require "cases/helper" +require 'models/post' require 'models/author' require 'models/topic' require 'models/reply' @@ -12,7 +13,6 @@ require 'models/auto_id' require 'models/column_name' require 'models/subscriber' require 'models/keyboard' -require 'models/post' require 'models/comment' require 'models/minimalistic' require 'models/warehouse_thing' @@ -1115,7 +1115,7 @@ class BasicsTest < ActiveRecord::TestCase Time.zone = nil Topic.skip_time_zone_conversion_for_attributes = [] end - + def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion ActiveRecord::Base.time_zone_aware_attributes = true ActiveRecord::Base.default_timezone = :utc @@ -1439,7 +1439,7 @@ class BasicsTest < ActiveRecord::TestCase topic = Topic.create("content" => myobj).reload assert_equal(myobj, topic.content) end - + def test_serialized_string_attribute myobj = "Yes" topic = Topic.create("content" => myobj).reload diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index c157ceb..f1bede9 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -1,4 +1,5 @@ require "cases/helper" +require 'models/post' require 'models/author' require 'models/categorization' require 'models/comment' @@ -7,7 +8,6 @@ require 'models/topic' require 'models/reply' require 'models/entrant' require 'models/developer' -require 'models/post' require 'models/customer' require 'models/job' require 'models/categorization' @@ -94,16 +94,16 @@ class FinderTest < ActiveRecord::TestCase assert_raise(NoMethodError) { Topic.exists?([1,2]) } end - + def test_exists_returns_true_with_one_record_and_no_args assert Topic.exists? end - + def test_does_not_exist_with_empty_table_and_no_args_given Topic.delete_all assert !Topic.exists? end - + def test_exists_with_aggregate_having_three_mappings existing_address = customers(:david).address assert Customer.exists?(:address => existing_address) diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 2f660a3..d8246f4 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -1,9 +1,9 @@ require "cases/helper" +require 'models/post' require 'models/author' require 'models/developer' require 'models/project' require 'models/comment' -require 'models/post' require 'models/category' class MethodScopingTest < ActiveRecord::TestCase -- 1.6.4.2 From ab6d295ce41ba2aad400474dcf45052e7b631b53 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 8 Aug 2009 14:44:23 -0700 Subject: [PATCH 172/779] Fix caller in assert_redirected_to deprecation warning. [#2932 state:committed] --- .../assertions/response_assertions.rb | 4 +++- actionpack/test/controller/redirect_test.rb | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb index 989be2d..931a031 100644 --- a/actionpack/lib/action_controller/assertions/response_assertions.rb +++ b/actionpack/lib/action_controller/assertions/response_assertions.rb @@ -64,7 +64,9 @@ module ActionController # Support partial arguments for hash redirections if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash) if options.all? {|(key, value)| @response.redirected_to[key] == value} - ::ActiveSupport::Deprecation.warn("Using assert_redirected_to with partial hash arguments is deprecated. Specify the full set arguments instead", caller) + callstack = caller.dup + callstack.slice!(0, 2) + ::ActiveSupport::Deprecation.warn("Using assert_redirected_to with partial hash arguments is deprecated. Specify the full set arguments instead", callstack) return true end end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index c457486..e80502b 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -236,7 +236,7 @@ class RedirectTest < ActionController::TestCase def test_redirect_with_partial_params get :module_redirect - assert_deprecated do + assert_deprecated(/test_redirect_with_partial_params/) do assert_redirected_to :action => 'hello_world' end end -- 1.6.4.2 From 791c3880393c671c3b101ea9b12836070df7a64d Mon Sep 17 00:00:00 2001 From: Marshall Huss Date: Mon, 18 May 2009 18:34:44 -0400 Subject: [PATCH 173/779] HTTP proxy support [#2133 state:committed] Signed-off-by: Jeremy Kemper --- activeresource/CHANGELOG | 6 ++ activeresource/lib/active_resource/base.rb | 22 ++++++++ activeresource/lib/active_resource/connection.rb | 18 +++++-- activeresource/test/base_test.rb | 64 ++++++++++++++++++++++ activeresource/test/connection_test.rb | 10 ++++ activeresource/test/fixtures/proxy.rb | 4 ++ 6 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 activeresource/test/fixtures/proxy.rb diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index 1142416..00f4bd1 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,7 +1,13 @@ +*2.3.4 (pending)* + +* HTTP proxy support. #2133 [Marshall Huss, Sébastien Dabet] + + *2.3.3 (July 12, 2009)* * No changes, just a version bump. + *2.3.2 [Final] (March 15, 2009)* * Nothing new, just included in 2.3.2 diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 4a4ee9f..aa6f599 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -246,6 +246,22 @@ module ActiveResource end end + # Gets the \proxy variable if a proxy is required + def proxy + # Not using superclass_delegating_reader. See +site+ for explanation + if defined?(@proxy) + @proxy + elsif superclass != Object && superclass.proxy + superclass.proxy.dup.freeze + end + end + + # Sets the URI of the http proxy to the value in the +proxy+ argument. + def proxy=(proxy) + @connection = nil + @proxy = proxy.nil? ? nil : create_proxy_uri_from(proxy) + end + # Gets the \user for REST HTTP authentication. def user # Not using superclass_delegating_reader. See +site+ for explanation @@ -321,6 +337,7 @@ module ActiveResource def connection(refresh = false) if defined?(@connection) || superclass == Object @connection = Connection.new(site, format) if refresh || @connection.nil? + @connection.proxy = proxy if proxy @connection.user = user if user @connection.password = password if password @connection.timeout = timeout if timeout @@ -611,6 +628,11 @@ module ActiveResource site.is_a?(URI) ? site.dup : URI.parse(site) end + # Accepts a URI and creates the proxy URI from that. + def create_proxy_uri_from(proxy) + proxy.is_a?(URI) ? proxy.dup : URI.parse(proxy) + end + # contains a set of the current prefix parameters. def prefix_parameters @prefix_parameters ||= prefix_source.scan(/:\w+/).map { |key| key[1..-1].to_sym }.to_set diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb index 943c9ef..45f9b71 100644 --- a/activeresource/lib/active_resource/connection.rb +++ b/activeresource/lib/active_resource/connection.rb @@ -70,7 +70,7 @@ module ActiveResource :delete => 'Accept' } - attr_reader :site, :user, :password, :timeout + attr_reader :site, :user, :password, :timeout, :proxy attr_accessor :format class << self @@ -95,7 +95,12 @@ module ActiveResource @password = URI.decode(@site.password) if @site.password end - # Set user for remote service. + # Set the proxy for remote service. + def proxy=(proxy) + @proxy = proxy.is_a?(URI) ? proxy : URI.parse(proxy) + end + + # Set the user for remote service. def user=(user) @user = user end @@ -186,8 +191,13 @@ module ActiveResource # Creates new Net::HTTP instance for communication with # remote service and resources. def http - http = Net::HTTP.new(@site.host, @site.port) - http.use_ssl = @site.is_a?(URI::HTTPS) + http = + if @proxy + Net::HTTP.new(@site.host, @site.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password) + else + Net::HTTP.new(@site.host, @site.port) + end + http.use_ssl = @site.is_a?(URI::HTTPS) http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl? http.read_timeout = @timeout if @timeout # If timeout is not set, the default Net::HTTP timeout (60s) is used. http diff --git a/activeresource/test/base_test.rb b/activeresource/test/base_test.rb index 6ed6f1a..a263153 100644 --- a/activeresource/test/base_test.rb +++ b/activeresource/test/base_test.rb @@ -3,6 +3,7 @@ require "fixtures/person" require "fixtures/customer" require "fixtures/street_address" require "fixtures/beast" +require "fixtures/proxy" class BaseTest < Test::Unit::TestCase def setup @@ -124,6 +125,28 @@ class BaseTest < Test::Unit::TestCase assert_nil actor.site end + def test_proxy_accessor_accepts_uri_or_string_argument + proxy = URI.parse('http://localhost') + + assert_nothing_raised { Person.proxy = 'http://localhost' } + assert_equal proxy, Person.proxy + + assert_nothing_raised { Person.proxy = proxy } + assert_equal proxy, Person.proxy + end + + def test_should_use_proxy_prefix_and_credentials + assert_equal 'http://user:password@proxy.local:3000', ProxyResource.proxy.to_s + end + + def test_proxy_variable_can_be_reset + actor = Class.new(ActiveResource::Base) + assert_nil actor.site + actor.proxy = 'http://localhost:31337' + actor.proxy = nil + assert_nil actor.site + end + def test_should_accept_setting_user Forum.user = 'david' assert_equal('david', Forum.user) @@ -220,6 +243,47 @@ class BaseTest < Test::Unit::TestCase assert_equal fruit.site, apple.site, 'subclass did not adopt changes from parent class' end + def test_proxy_reader_uses_superclass_site_until_written + # Superclass is Object so returns nil. + assert_nil ActiveResource::Base.proxy + assert_nil Class.new(ActiveResource::Base).proxy + + # Subclass uses superclass proxy. + actor = Class.new(Person) + assert_equal Person.proxy, actor.proxy + + # Subclass returns frozen superclass copy. + assert !Person.proxy.frozen? + assert actor.proxy.frozen? + + # Changing subclass proxy doesn't change superclass site. + actor.proxy = 'http://localhost:31337' + assert_not_equal Person.proxy, actor.proxy + + # Changed subclass proxy is not frozen. + assert !actor.proxy.frozen? + + # Changing superclass proxy doesn't overwrite subclass site. + Person.proxy = 'http://somewhere.else' + assert_not_equal Person.proxy, actor.proxy + + # Changing superclass proxy after subclassing changes subclass site. + jester = Class.new(actor) + actor.proxy = 'http://nomad' + assert_equal actor.proxy, jester.proxy + assert jester.proxy.frozen? + + # Subclasses are always equal to superclass proxy when not overridden + fruit = Class.new(ActiveResource::Base) + apple = Class.new(fruit) + + fruit.proxy = 'http://market' + assert_equal fruit.proxy, apple.proxy, 'subclass did not adopt changes from parent class' + + fruit.proxy = 'http://supermarket' + assert_equal fruit.proxy, apple.proxy, 'subclass did not adopt changes from parent class' + end + def test_user_reader_uses_superclass_user_until_written # Superclass is Object so returns nil. assert_nil ActiveResource::Base.user diff --git a/activeresource/test/connection_test.rb b/activeresource/test/connection_test.rb index 831fbc4..60a8204 100644 --- a/activeresource/test/connection_test.rb +++ b/activeresource/test/connection_test.rb @@ -101,6 +101,16 @@ class ConnectionTest < Test::Unit::TestCase assert_equal site, @conn.site end + def test_proxy_accessor_accepts_uri_or_string_argument + proxy = URI.parse("http://proxy_user:proxy_password@proxy.local:4242") + + assert_nothing_raised { @conn.proxy = "http://proxy_user:proxy_password@proxy.local:4242" } + assert_equal proxy, @conn.proxy + + assert_nothing_raised { @conn.proxy = proxy } + assert_equal proxy, @conn.proxy + end + def test_timeout_accessor @conn.timeout = 5 assert_equal 5, @conn.timeout diff --git a/activeresource/test/fixtures/proxy.rb b/activeresource/test/fixtures/proxy.rb new file mode 100644 index 0000000..bb8e015 --- /dev/null +++ b/activeresource/test/fixtures/proxy.rb @@ -0,0 +1,4 @@ +class ProxyResource < ActiveResource::Base + self.site = "http://localhost" + self.proxy = "http://user:password@proxy.local:3000" +end \ No newline at end of file -- 1.6.4.2 From cd5e7843894298f7d756492dbdadb26a50e35a3c Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 9 Aug 2009 11:11:34 +1200 Subject: [PATCH 174/779] Depend on rubygems 1.3.2 Also move the min_version definition up a line so it's present in the rescue block down below. --- railties/environments/boot.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/environments/boot.rb b/railties/environments/boot.rb index 0ad0f78..dd5e3b6 100644 --- a/railties/environments/boot.rb +++ b/railties/environments/boot.rb @@ -82,8 +82,8 @@ module Rails end def load_rubygems + min_version = '1.3.2' require 'rubygems' - min_version = '1.3.1' unless rubygems_version >= min_version $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.) exit 1 -- 1.6.4.2 From 679a0bf17fa9d74a4475753e9075bed8aea59e9a Mon Sep 17 00:00:00 2001 From: Steve St. Martin Date: Sat, 8 Aug 2009 18:44:33 -0400 Subject: [PATCH 175/779] Update truncate documentation / examples to more clearly demonstrate its actual behavior [#3016 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_view/helpers/text_helper.rb | 17 +++++++++-------- 1 files changed, 9 insertions(+), 8 deletions(-) diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 8463af9..8f1ddb9 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -33,30 +33,31 @@ module ActionView end # Truncates a given +text+ after a given :length if +text+ is longer than :length - # (defaults to 30). The last characters will be replaced with the :omission (defaults to "..."). + # (defaults to 30). The last characters will be replaced with the :omission (defaults to "...") + # for a total length not exceeding :length. # # ==== Examples # # truncate("Once upon a time in a world far far away") - # # => Once upon a time in a world f... + # # => Once upon a time in a world... # # truncate("Once upon a time in a world far far away", :length => 14) # # => Once upon a... # # truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)") - # # => And they found that many (clipped) + # # => And they found t(clipped) # - # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 15) - # # => And they found... (continued) + # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 25) + # # => And they f... (continued) # # You can still use truncate with the old API that accepts the # +length+ as its optional second and the +ellipsis+ as its # optional third parameter: # truncate("Once upon a time in a world far far away", 14) - # # => Once upon a time in a world f... + # # => Once upon a... # - # truncate("And they found that many people were sleeping better.", 15, "... (continued)") - # # => And they found... (continued) + # truncate("And they found that many people were sleeping better.", 25, "... (continued)") + # # => And they f... (continued) def truncate(text, *args) options = args.extract_options! unless args.empty? -- 1.6.4.2 From d39c45690e501e7572de2f70f68224bc95e6bf01 Mon Sep 17 00:00:00 2001 From: Nick Quaranto and Josh Nichols Date: Sat, 8 Aug 2009 18:10:55 -0400 Subject: [PATCH 176/779] Adding a deprecation warning for output.flush when rendering a proc or lambda [#2893 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/base.rb | 1 - actionpack/lib/action_controller/response.rb | 6 ++++++ actionpack/test/controller/rack_test.rb | 19 ++++++++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 0801bd6..1d8cc14 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -819,7 +819,6 @@ module ActionController #:nodoc: # render :text => proc { |response, output| # 10_000_000.times do |i| # output.write("This is line #{i}\n") - # output.flush # end # } # diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb index def03fa..afee6a4 100644 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_controller/response.rb @@ -166,6 +166,12 @@ module ActionController # :nodoc: str end + def flush #:nodoc: + ActiveSupport::Deprecation.warn( + 'Calling output.flush is no longer needed for streaming output ' + + 'because ActionController::Response automatically handles it', caller) + end + def set_cookie(key, value) if value.has_key?(:http_only) ActiveSupport::Deprecation.warn( diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb index b550d3d..aeace2b 100644 --- a/actionpack/test/controller/rack_test.rb +++ b/actionpack/test/controller/rack_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' -class BaseRackTest < Test::Unit::TestCase +class BaseRackTest < ActiveSupport::TestCase def setup @env = { "HTTP_MAX_FORWARDS" => "10", @@ -261,6 +261,23 @@ class RackResponseTest < BaseRackTest body.each { |part| parts << part } assert_equal ["0", "1", "2", "3", "4"], parts end + + def test_streaming_block_with_flush_is_deprecated + @response.body = Proc.new do |response, output| + 5.times do |n| + output.write(n) + output.flush + end + end + + assert_deprecated(/output.flush is no longer needed/) do + @response.prepare! + status, headers, body = @response.to_a + + parts = [] + body.each { |part| parts << part } + end + end end class RackResponseHeadersTest < BaseRackTest -- 1.6.4.2 From 389449d9aef3f32842a2022130e5be552b039124 Mon Sep 17 00:00:00 2001 From: Jan Schwenzien Date: Sun, 9 Aug 2009 01:28:07 +0100 Subject: [PATCH 177/779] Fix HTTP basic authentication for long credentials [#2572 state:resolved] Signed-off-by: Pratik Naik --- .../lib/action_controller/http_authentication.rb | 2 +- .../controller/http_basic_authentication_test.rb | 25 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index 88c289b..1bea70f 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -139,7 +139,7 @@ module ActionController end def decode_credentials(request) - ActiveSupport::Base64.decode64(authorization(request).split.last || '') + ActiveSupport::Base64.decode64(authorization(request).split(' ', 2).last || '') end def encode_credentials(user_name, password) diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index fbc94a0..23688ca 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -4,6 +4,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase class DummyController < ActionController::Base before_filter :authenticate, :only => :index before_filter :authenticate_with_request, :only => :display + before_filter :authenticate_long_credentials, :only => :show def index render :text => "Hello Secret" @@ -12,6 +13,10 @@ class HttpBasicAuthenticationTest < ActionController::TestCase def display render :text => 'Definitely Maybe' end + + def show + render :text => 'Only for loooooong credentials' + end private @@ -28,6 +33,12 @@ class HttpBasicAuthenticationTest < ActionController::TestCase request_http_basic_authentication("SuperSecret") end end + + def authenticate_long_credentials + authenticate_or_request_with_http_basic do |username, password| + username == '1234567890123456789012345678901234567890' && password == '1234567890123456789012345678901234567890' + end + end end AUTH_HEADERS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION', 'REDIRECT_X_HTTP_AUTHORIZATION'] @@ -42,6 +53,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_response :success assert_equal 'Hello Secret', @response.body, "Authentication failed for request header #{header}" end + test "successful authentication with #{header.downcase} and long credentials" do + @request.env[header] = encode_credentials('1234567890123456789012345678901234567890', '1234567890123456789012345678901234567890') + get :show + + assert_response :success + assert_equal 'Only for loooooong credentials', @response.body, "Authentication failed for request header #{header} and long credentials" + end end AUTH_HEADERS.each do |header| @@ -52,6 +70,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_response :unauthorized assert_equal "HTTP Basic: Access denied.\n", @response.body, "Authentication didn't fail for request header #{header}" end + test "unsuccessful authentication with #{header.downcase} and long credentials" do + @request.env[header] = encode_credentials('h4x0rh4x0rh4x0rh4x0rh4x0rh4x0rh4x0rh4x0r', 'worldworldworldworldworldworldworldworld') + get :show + + assert_response :unauthorized + assert_equal "HTTP Basic: Access denied.\n", @response.body, "Authentication didn't fail for request header #{header} and long credentials" + end end test "authentication request without credential" do -- 1.6.4.2 From 407fbb50907b34f062c7670b570c44abeb21e2f6 Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 7 Aug 2009 22:55:19 -0400 Subject: [PATCH 178/779] Adding :from scoping to ActiveRecord calculations Signed-off-by: Michael Koziarski --- activerecord/lib/active_record/calculations.rb | 2 ++ activerecord/test/cases/calculations_test.rb | 6 ++++++ activerecord/test/models/organization.rb | 2 ++ 3 files changed, 10 insertions(+), 0 deletions(-) diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 7af97d7..6eee401 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -190,6 +190,8 @@ module ActiveRecord sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group] if options[:from] sql << " FROM #{options[:from]} " + elsif scope && scope[:from] + sql << " FROM #{scope[:from]} " else sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround sql << " FROM #{connection.quote_table_name(table_name)} " diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index b4f76cb..f428052 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -5,6 +5,8 @@ require 'models/edge' require 'models/owner' require 'models/pet' require 'models/toy' +require 'models/club' +require 'models/organization' Company.has_many :accounts @@ -226,6 +228,10 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal 15, companies(:rails_core).companies.sum(:id) end + def test_should_sum_scoped_field_with_from + assert_equal Club.count, Organization.clubs.count + end + def test_should_sum_scoped_field_with_conditions assert_equal 8, companies(:rails_core).companies.sum(:id, :conditions => 'id > 7') end diff --git a/activerecord/test/models/organization.rb b/activerecord/test/models/organization.rb index d79d503..c857261 100644 --- a/activerecord/test/models/organization.rb +++ b/activerecord/test/models/organization.rb @@ -1,4 +1,6 @@ class Organization < ActiveRecord::Base has_many :member_details has_many :members, :through => :member_details + + named_scope :clubs, { :from => 'clubs' } end \ No newline at end of file -- 1.6.4.2 From 819c347f436f54acea5821289c6340da863bf138 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Tue, 14 Apr 2009 01:12:55 -0400 Subject: [PATCH 179/779] Enumerable#sum now works will all enumerables, even if they don't respond to :size [#2489 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/active_support/core_ext/enumerable.rb | 6 ++---- activesupport/test/core_ext/enumerable_test.rb | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index a7eaccf..2c5f59b 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -55,12 +55,10 @@ module Enumerable # [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0) # def sum(identity = 0, &block) - return identity unless size > 0 - if block_given? - map(&block).sum + map(&block).sum(identity) else - inject { |sum, element| sum + element } + inject { |sum, element| sum + element } || identity end end diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index 92db977..026c8c8 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -58,6 +58,10 @@ class EnumerableTests < Test::Unit::TestCase assert_equal Payment.new(0), [].sum(Payment.new(0)) end + def test_enumerable_sums + assert_equal 10, (1..4).sum + end + def test_each_with_object result = %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase } assert_equal({'foo' => 'FOO', 'bar' => 'BAR'}, result) -- 1.6.4.2 From 796b7c6ce62559be253b8a2c8ff2a6e447a60d7f Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 9 Aug 2009 13:07:50 +1200 Subject: [PATCH 180/779] Don't call additional methods on builders passed to the atom_feed helper. Additionally, actually test that the atom_feed helper works with :xml as an option. [#1836 state:committed] --- .../lib/action_view/helpers/atom_feed_helper.rb | 2 +- actionpack/test/template/atom_feed_helper_test.rb | 29 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index dc44975..9951e11 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -98,7 +98,7 @@ module ActionView options[:schema_date] = "2005" # The Atom spec copyright date end - xml = options[:xml] || eval("xml", block.binding) + xml = options.delete(:xml) || eval("xml", block.binding) xml.instruct! if options[:instruct] options[:instruct].each do |target,attrs| diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index 8a00a39..01a4178 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -150,6 +150,26 @@ class ScrollsController < ActionController::Base end end EOT + FEEDS["provide_builder"] = <<-'EOT' + # we pass in the new_xml to the helper so it doesn't + # call anything on the original builder + new_xml = Builder::XmlMarkup.new(:target=>'') + atom_feed(:xml => new_xml) do |feed| + feed.title("My great blog!") + feed.updated((@scrolls.first.created_at)) + + for scroll in @scrolls + feed.entry(scroll) do |entry| + entry.title(scroll.title) + entry.content(scroll.body, :type => 'html') + + entry.author do |author| + author.name("DHH") + end + end + end + end + EOT def index @scrolls = [ Scroll.new(1, "1", "Hello One", "Something COOL!", Time.utc(2007, 12, 12, 15), Time.utc(2007, 12, 12, 15)), @@ -194,6 +214,15 @@ class AtomFeedTest < ActionController::TestCase end end + def test_providing_builder_to_atom_feed + with_restful_routing(:scrolls) do + get :index, :id=>"provide_builder" + # because we pass in the non-default builder, the content generated by the + # helper should go 'nowhere'. Leaving the response body blank. + assert @response.body.blank? + end + end + def test_entry_with_prefilled_options_should_use_those_instead_of_querying_the_record with_restful_routing(:scrolls) do get :index, :id => "entry_options" -- 1.6.4.2 From ac9f9a9c3e61e977a1614f484648d9334d0f67ed Mon Sep 17 00:00:00 2001 From: Hugo Peixoto Date: Sun, 9 Aug 2009 03:49:57 +0100 Subject: [PATCH 181/779] MySQL: fix diacritic uniqueness test by setting the default character set and collation to utf8/utf8_unicode_ci [#2883 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/Rakefile | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 7855053..632ea40 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -53,8 +53,8 @@ end namespace :mysql do desc 'Build the MySQL test databases' task :build_databases do - %x( mysqladmin --user=#{MYSQL_DB_USER} create activerecord_unittest ) - %x( mysqladmin --user=#{MYSQL_DB_USER} create activerecord_unittest2 ) + %x( echo "create DATABASE activerecord_unittest DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci " | mysql --user=#{MYSQL_DB_USER}) + %x( echo "create DATABASE activerecord_unittest2 DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_unicode_ci " | mysql --user=#{MYSQL_DB_USER}) end desc 'Drop the MySQL test databases' -- 1.6.4.2 From a25296ab0543f60944a1d6c36314758c5bc02483 Mon Sep 17 00:00:00 2001 From: Rob Date: Fri, 1 May 2009 23:22:09 +0100 Subject: [PATCH 182/779] Fix binary fixture test on Windows [#2597 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/test/cases/fixtures_test.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index b07d4f3..eb3f03c 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -185,7 +185,7 @@ class FixturesTest < ActiveRecord::TestCase def test_binary_in_fixtures assert_equal 1, @binaries.size - data = File.read(ASSETS_ROOT + "/flowers.jpg") + data = File.open(ASSETS_ROOT + "/flowers.jpg", 'rb') { |f| f.read } data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding) data.freeze assert_equal data, @flowers.data -- 1.6.4.2 From 9aa9bad024253a7ab2adee63df8185dc876393f9 Mon Sep 17 00:00:00 2001 From: Tristan Dunn Date: Sat, 8 Aug 2009 18:46:06 -0400 Subject: [PATCH 183/779] Don't define a default primary key in the schema dumper. [#1908 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/schema_dumper.rb | 3 +-- activerecord/test/cases/schema_dumper_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 651cd36..68a45a0 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -84,7 +84,6 @@ HEADER elsif @connection.respond_to?(:primary_key) pk = @connection.primary_key(table) end - pk ||= 'id' tbl.print " create_table #{table.inspect}" if columns.detect { |c| c.name == pk } @@ -180,4 +179,4 @@ HEADER end end end -end \ No newline at end of file +end diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 972700d..0d25bb7 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -190,4 +190,12 @@ class SchemaDumperTest < ActiveRecord::TestCase output = stream.string assert_match %r{:precision => 3,[[:space:]]+:scale => 2,[[:space:]]+:default => 2.78}, output end + + def test_schema_dump_keeps_id_column_when_id_is_false_and_id_column_added + output = standard_dump + match = output.match(%r{create_table "goofy_string_id"(.*)do.*\n(.*)\n}) + assert_not_nil(match, "goofy_string_id table not found") + assert_match %r(:id => false), match[1], "no table id not preserved" + assert_match %r{t.string[[:space:]]+"id",[[:space:]]+:null => false$}, match[2], "non-primary key id column not preserved" + end end -- 1.6.4.2 From 0b95a2afab88851eea78926eb91462002ff8db9a Mon Sep 17 00:00:00 2001 From: Rich Bradley Date: Sat, 8 Aug 2009 23:29:38 -0700 Subject: [PATCH 184/779] Fix for nested :include with namespaced models. [#260 state:committed] --- activerecord/lib/active_record/associations.rb | 2 +- activerecord/test/cases/modules_test.rb | 13 +++++++++++++ activerecord/test/models/company_in_module.rb | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 66db63a..388a785 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1838,7 +1838,7 @@ module ActiveRecord descendant end.flatten.compact - remove_duplicate_results!(reflection.class_name.constantize, parent_records, associations[name]) unless parent_records.empty? + remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty? end end end diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb index 283333f..8360416 100644 --- a/activerecord/test/cases/modules_test.rb +++ b/activerecord/test/cases/modules_test.rb @@ -36,4 +36,17 @@ class ModulesTest < ActiveRecord::TestCase assert_equal 'companies', MyApplication::Business::Client.table_name, 'table_name for ActiveRecord model subclass' assert_equal 'company_contacts', MyApplication::Business::Client::Contact.table_name, 'table_name for ActiveRecord model enclosed by another ActiveRecord model' end + + def test_eager_loading_in_modules + # need to add an eager loading condition to force the eager loading model into + # the old join model, to test that. See http://dev.rubyonrails.org/ticket/9640 + client_join_loaded = MyApplication::Business::Client.find(3, :include => {:firm => :account}, :conditions => 'accounts.id IS NOT NULL') + client_sequential_loaded = MyApplication::Business::Client.find(3, :include => {:firm => :account}) + + [client_join_loaded, client_sequential_loaded].each do |client| + assert_no_queries do + assert_not_nil(client.firm.account) + end + end + end end diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb index 7f02403..e071525 100644 --- a/activerecord/test/models/company_in_module.rb +++ b/activerecord/test/models/company_in_module.rb @@ -11,7 +11,7 @@ module MyApplication has_many :clients_like_ms, :conditions => "name = 'Microsoft'", :class_name => "Client", :order => "id" has_many :clients_using_sql, :class_name => "Client", :finder_sql => 'SELECT * FROM companies WHERE client_of = #{id}' - has_one :account, :dependent => :destroy + has_one :account, :class_name => 'MyApplication::Billing::Account', :dependent => :destroy end class Client < Company -- 1.6.4.2 From 7d40ba1cbf670a5161fbb1df962c98aaffff4e14 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sat, 8 Aug 2009 18:40:54 -0300 Subject: [PATCH 185/779] Added benchmark to 2-3 --- actionpack/examples/minimal.rb | 28 ++++++++++++++++++++++++++++ 1 files changed, 28 insertions(+), 0 deletions(-) create mode 100644 actionpack/examples/minimal.rb diff --git a/actionpack/examples/minimal.rb b/actionpack/examples/minimal.rb new file mode 100644 index 0000000..8878a00 --- /dev/null +++ b/actionpack/examples/minimal.rb @@ -0,0 +1,28 @@ +$:.push File.join(File.dirname(__FILE__), "..", "lib") +require "action_controller" + +class Runner + def initialize(app) + @app = app + end + + def call(env) + env['n'].to_i.times { @app.call(env) } + @app.call(env).tap { |response| report(env, response) } + end + + 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 + + def self.run(app, n, label = nil) + puts '=' * label.size, label, '=' * label.size if label + env = Rack::MockRequest.env_for("/").merge('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout) + t = Benchmark.realtime { new(app).call(env) } + puts "%d ms / %d req = %.1f usec/req" % [10**3 * t, n, 10**6 * t / n] + puts + end +end -- 1.6.4.2 From 3cad1df22e823d3debbbd15c02653f4f1fe69026 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sat, 8 Aug 2009 21:54:58 -0300 Subject: [PATCH 186/779] Benchmarks --- actionpack/examples/minimal.rb | 48 ++++++++++++++++++++-- actionpack/examples/views/_collection.erb | 1 + actionpack/examples/views/_hello.erb | 1 + actionpack/examples/views/_many_partials.erb | 10 +++++ actionpack/examples/views/_partial.erb | 10 +++++ actionpack/examples/views/layouts/alt.html.erb | 1 + actionpack/examples/views/layouts/kaigi.html.erb | 1 + actionpack/examples/views/template.html.erb | 1 + 8 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 actionpack/examples/views/_collection.erb create mode 100644 actionpack/examples/views/_hello.erb create mode 100644 actionpack/examples/views/_many_partials.erb create mode 100644 actionpack/examples/views/_partial.erb create mode 100644 actionpack/examples/views/layouts/alt.html.erb create mode 100644 actionpack/examples/views/layouts/kaigi.html.erb create mode 100644 actionpack/examples/views/template.html.erb diff --git a/actionpack/examples/minimal.rb b/actionpack/examples/minimal.rb index 8878a00..477e377 100644 --- a/actionpack/examples/minimal.rb +++ b/actionpack/examples/minimal.rb @@ -1,4 +1,5 @@ $:.push File.join(File.dirname(__FILE__), "..", "lib") +$:.push File.join(File.dirname(__FILE__), "..", "..", "activesupport", "lib") require "action_controller" class Runner @@ -13,16 +14,55 @@ class Runner def report(env, response) out = env['rack.errors'] - out.puts response[0], response[1].to_yaml, '---' - response[2].each { |part| out.puts part } + p response.headers + out.puts response.status, response.headers.to_yaml, '---' + response.body.each { |part| out.puts part } out.puts '---' end - def self.run(app, n, label = nil) + def self.run(app, n, label = nil, uri = "/") puts '=' * label.size, label, '=' * label.size if label - env = Rack::MockRequest.env_for("/").merge('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout) + env = Rack::MockRequest.env_for(uri).merge('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout) t = Benchmark.realtime { new(app).call(env) } puts "%d ms / %d req = %.1f usec/req" % [10**3 * t, n, 10**6 * t / n] puts end end + +N = (ENV['N'] || 1000).to_i + +class BasePostController < ActionController::Base + append_view_path "#{File.dirname(__FILE__)}/views" + + def index + render :text => 'Hello' + end + + def partial + render :partial => "/partial" + end + + def many_partials + render :partial => "/many_partials" + end + + def partial_collection + render :partial => "/collection", :collection => [1,2,3,4,5,6,7,8,9,10] + end + + def show_template + render :template => "template" + end +end + +# p BasePostController.call(Rack::MockRequest.env_for("/?action=index").merge("REQUEST_URI" => "/")).body + +Runner.run(BasePostController, N, 'index', "/?action=index") +Runner.run(BasePostController, N, 'partial', "/?action=partial") +Runner.run(BasePostController, N, 'many partials', "/?action=many_partials") +Runner.run(BasePostController, N, 'collection', "/?action=partial_collection") +Runner.run(BasePostController, N, 'template', "/?action=show_template") +# Runner.run(BasePostController.action(:many_partials), N, 'index') +# Runner.run(BasePostController.action(:many_partials), N, 'many_partials') +# Runner.run(BasePostController.action(:partial_collection), N, 'collection') +# Runner.run(BasePostController.action(:show_template), N, 'template') diff --git a/actionpack/examples/views/_collection.erb b/actionpack/examples/views/_collection.erb new file mode 100644 index 0000000..bcfe958 --- /dev/null +++ b/actionpack/examples/views/_collection.erb @@ -0,0 +1 @@ +<%= collection %> \ No newline at end of file diff --git a/actionpack/examples/views/_hello.erb b/actionpack/examples/views/_hello.erb new file mode 100644 index 0000000..5ab2f8a --- /dev/null +++ b/actionpack/examples/views/_hello.erb @@ -0,0 +1 @@ +Hello \ No newline at end of file diff --git a/actionpack/examples/views/_many_partials.erb b/actionpack/examples/views/_many_partials.erb new file mode 100644 index 0000000..7e379d4 --- /dev/null +++ b/actionpack/examples/views/_many_partials.erb @@ -0,0 +1,10 @@ +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> +<%= render :partial => '/hello' %> \ No newline at end of file diff --git a/actionpack/examples/views/_partial.erb b/actionpack/examples/views/_partial.erb new file mode 100644 index 0000000..3ca8e80 --- /dev/null +++ b/actionpack/examples/views/_partial.erb @@ -0,0 +1,10 @@ +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> +<%= "Hello" %> diff --git a/actionpack/examples/views/layouts/alt.html.erb b/actionpack/examples/views/layouts/alt.html.erb new file mode 100644 index 0000000..c481633 --- /dev/null +++ b/actionpack/examples/views/layouts/alt.html.erb @@ -0,0 +1 @@ ++ <%= yield %> + \ No newline at end of file diff --git a/actionpack/examples/views/layouts/kaigi.html.erb b/actionpack/examples/views/layouts/kaigi.html.erb new file mode 100644 index 0000000..274607a --- /dev/null +++ b/actionpack/examples/views/layouts/kaigi.html.erb @@ -0,0 +1 @@ +Hello <%= yield %> Goodbye \ No newline at end of file diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb new file mode 100644 index 0000000..5ab2f8a --- /dev/null +++ b/actionpack/examples/views/template.html.erb @@ -0,0 +1 @@ +Hello \ No newline at end of file -- 1.6.4.2 From ee8fe3ae4e7c3eed99c77ccb3872c972c12a4102 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 9 Aug 2009 03:56:29 -0300 Subject: [PATCH 187/779] Update bench harness in 2.3 to match master output --- actionpack/examples/minimal.rb | 57 ++++++++++++++++++++++++++------------- 1 files changed, 38 insertions(+), 19 deletions(-) diff --git a/actionpack/examples/minimal.rb b/actionpack/examples/minimal.rb index 477e377..c777454 100644 --- a/actionpack/examples/minimal.rb +++ b/actionpack/examples/minimal.rb @@ -3,8 +3,12 @@ $:.push File.join(File.dirname(__FILE__), "..", "..", "activesupport", "lib") require "action_controller" class Runner - def initialize(app) - @app = app + def initialize(app, output) + @app, @output = app, output + end + + def puts(*) + super if @output end def call(env) @@ -13,17 +17,24 @@ class Runner end def report(env, response) - out = env['rack.errors'] - p response.headers - out.puts response.status, response.headers.to_yaml, '---' - response.body.each { |part| out.puts part } - out.puts '---' + if ENV["DEBUG"] + out = env['rack.errors'] + p response.headers + out.puts response.status, response.headers.to_yaml, '---' + response.body.each { |part| out.puts part } + out.puts '---' + end + end + + def self.puts(*) + super if @output end - def self.run(app, n, label = nil, uri = "/") - puts '=' * label.size, label, '=' * label.size if label + def self.run(app, n, label = nil, uri = "/", output = true) + @output = output + puts label, '=' * label.size if label env = Rack::MockRequest.env_for(uri).merge('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout) - t = Benchmark.realtime { new(app).call(env) } + t = Benchmark.realtime { new(app, output).call(env) } puts "%d ms / %d req = %.1f usec/req" % [10**3 * t, n, 10**6 * t / n] puts end @@ -57,12 +68,20 @@ end # p BasePostController.call(Rack::MockRequest.env_for("/?action=index").merge("REQUEST_URI" => "/")).body -Runner.run(BasePostController, N, 'index', "/?action=index") -Runner.run(BasePostController, N, 'partial', "/?action=partial") -Runner.run(BasePostController, N, 'many partials', "/?action=many_partials") -Runner.run(BasePostController, N, 'collection', "/?action=partial_collection") -Runner.run(BasePostController, N, 'template', "/?action=show_template") -# Runner.run(BasePostController.action(:many_partials), N, 'index') -# Runner.run(BasePostController.action(:many_partials), N, 'many_partials') -# Runner.run(BasePostController.action(:partial_collection), N, 'collection') -# Runner.run(BasePostController.action(:show_template), N, 'template') +Runner.run(BasePostController, N, 'index', "/?action=index", false) +Runner.run(BasePostController, N, 'partial', "/?action=partial", false) +Runner.run(BasePostController, N, 'many partials', "/?action=many_partials", false) +Runner.run(BasePostController, N, 'collection', "/?action=partial_collection", false) +Runner.run(BasePostController, N, 'template', "/?action=show_template", false) + +(ENV["M"] || 1).to_i.times do + Runner.run(BasePostController, N, 'index', "/?action=index") + Runner.run(BasePostController, N, 'partial', "/?action=partial") + Runner.run(BasePostController, N, 'many partials', "/?action=many_partials") + Runner.run(BasePostController, N, 'collection', "/?action=partial_collection") + Runner.run(BasePostController, N, 'template', "/?action=show_template") +end + # Runner.run(BasePostController.action(:many_partials), N, 'index') + # Runner.run(BasePostController.action(:many_partials), N, 'many_partials') + # Runner.run(BasePostController.action(:partial_collection), N, 'collection') + # Runner.run(BasePostController.action(:show_template), N, 'template') -- 1.6.4.2 From 83cc7de2a47df199348cc47d2794a3f30ad4f4fb Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 9 Aug 2009 02:24:35 -0700 Subject: [PATCH 188/779] Setting connection timeout also affects Net::HTTP open_timeout. [#2947 state:resolved] --- activeresource/lib/active_resource/connection.rb | 26 ++++++++++++++++------ activeresource/test/connection_test.rb | 11 +++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb index 45f9b71..48b44b3 100644 --- a/activeresource/lib/active_resource/connection.rb +++ b/activeresource/lib/active_resource/connection.rb @@ -191,15 +191,27 @@ module ActiveResource # Creates new Net::HTTP instance for communication with # remote service and resources. def http - http = - if @proxy - Net::HTTP.new(@site.host, @site.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password) - else - Net::HTTP.new(@site.host, @site.port) - end + configure_http(new_http) + end + + def new_http + if @proxy + Net::HTTP.new(@site.host, @site.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password) + else + Net::HTTP.new(@site.host, @site.port) + end + end + + def configure_http(http) http.use_ssl = @site.is_a?(URI::HTTPS) http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl? - http.read_timeout = @timeout if @timeout # If timeout is not set, the default Net::HTTP timeout (60s) is used. + + # Net::HTTP timeouts default to 60 seconds. + if @timeout + http.open_timeout = @timeout + http.read_timeout = @timeout + end + http end diff --git a/activeresource/test/connection_test.rb b/activeresource/test/connection_test.rb index 60a8204..12e8058 100644 --- a/activeresource/test/connection_test.rb +++ b/activeresource/test/connection_test.rb @@ -185,6 +185,17 @@ class ConnectionTest < Test::Unit::TestCase assert_raise(ActiveResource::TimeoutError) { @conn.get('/people_timeout.xml') } end + def test_setting_timeout + http = Net::HTTP.new('') + + [10, 20].each do |timeout| + @conn.timeout = timeout + @conn.send(:configure_http, http) + assert_equal timeout, http.open_timeout + assert_equal timeout, http.read_timeout + end + end + def test_accept_http_header @http = mock('new Net::HTTP') @conn.expects(:http).returns(@http) -- 1.6.4.2 From 32c23552f547412081116c001b152bca78c9892e Mon Sep 17 00:00:00 2001 From: Dmitry Ratnikov Date: Sun, 9 Aug 2009 03:48:49 -0500 Subject: [PATCH 189/779] Changed to use klass instead of constantizing in assign_ids generated method [#260 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/associations.rb | 2 +- activerecord/test/cases/modules_test.rb | 39 ++++++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 388a785..7732bb5 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1293,7 +1293,7 @@ module ActiveRecord define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value| ids = (new_value || []).reject { |nid| nid.blank? } - send("#{reflection.name}=", reflection.class_name.constantize.find(ids)) + send("#{reflection.name}=", reflection.klass.find(ids)) end end end diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb index 8360416..4f559bc 100644 --- a/activerecord/test/cases/modules_test.rb +++ b/activerecord/test/cases/modules_test.rb @@ -4,6 +4,23 @@ require 'models/company_in_module' class ModulesTest < ActiveRecord::TestCase fixtures :accounts, :companies, :projects, :developers + def setup + # need to make sure Object::Firm and Object::Client are not defined, + # so that constantize will not be able to cheat when having to load namespaced classes + @undefined_consts = {} + + [:Firm, :Client].each do |const| + @undefined_consts.merge! const => Object.send(:remove_const, const) if Object.const_defined?(const) + end + end + + def teardown + # reinstate the constants that we undefined in the setup + @undefined_consts.each do |constant, value| + Object.send :const_set, constant, value unless value.nil? + end + end + def test_module_spanning_associations firm = MyApplication::Business::Firm.find(:first) assert !firm.clients.empty?, "Firm should have clients" @@ -37,13 +54,25 @@ class ModulesTest < ActiveRecord::TestCase assert_equal 'company_contacts', MyApplication::Business::Client::Contact.table_name, 'table_name for ActiveRecord model enclosed by another ActiveRecord model' end + def test_assign_ids + firm = MyApplication::Business::Firm.first + + assert_nothing_raised NameError, "Should be able to resolve all class constants via reflection" do + firm.client_ids = [MyApplication::Business::Client.first.id] + end + end + + # need to add an eager loading condition to force the eager loading model into + # the old join model, to test that. See http://dev.rubyonrails.org/ticket/9640 def test_eager_loading_in_modules - # need to add an eager loading condition to force the eager loading model into - # the old join model, to test that. See http://dev.rubyonrails.org/ticket/9640 - client_join_loaded = MyApplication::Business::Client.find(3, :include => {:firm => :account}, :conditions => 'accounts.id IS NOT NULL') - client_sequential_loaded = MyApplication::Business::Client.find(3, :include => {:firm => :account}) + clients = [] + + assert_nothing_raised NameError, "Should be able to resolve all class constants via reflection" do + clients << MyApplication::Business::Client.find(3, :include => {:firm => :account}, :conditions => 'accounts.id IS NOT NULL') + clients << MyApplication::Business::Client.find(3, :include => {:firm => :account}) + end - [client_join_loaded, client_sequential_loaded].each do |client| + clients.each do |client| assert_no_queries do assert_not_nil(client.firm.account) end -- 1.6.4.2 From 2d2216fadb0bb6eb6feebe697d3e7041f7d0d965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Jul 2009 11:07:11 +0200 Subject: [PATCH 190/779] Make http digest work with different server/browser combinations. [#3006 status:resolved] Signed-off-by: Pratik Naik --- .../lib/action_controller/http_authentication.rb | 3 +- .../controller/http_digest_authentication_test.rb | 35 ++++++++++++++++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index 1bea70f..ac87223 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -195,9 +195,10 @@ module ActionController return false unless password method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] + uri = credentials[:uri][0,1] == '/' ? request.request_uri : request.url [true, false].any? do |password_is_ha1| - expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1) + expected = expected_response(method, uri, credentials, password, password_is_ha1) expected == credentials[:response] end end diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 5cc47ee..6f6215e 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -129,7 +129,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'Definitely Maybe', @response.body end - test "authentication request with request-uri that doesn't match credentials digest-uri" do + test "authentication request with request-uri that doesn't match credentials digest-uri" do @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please') @request.env['REQUEST_URI'] = "/http_digest_authentication_test/dummy_digest/altered/uri" get :display @@ -138,10 +138,33 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal "Authentication Failed", @response.body end - test "authentication request with absolute uri" do - @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:uri => "http://test.host/http_digest_authentication_test/dummy_digest/display", + test "authentication request with absolute request uri (as in webrick)" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please') + @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest" + + get :display + + assert_response :success + assert assigns(:logged_in) + assert_equal 'Definitely Maybe', @response.body + end + + test "authentication request with absolute uri in credentials (as in IE)" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:url => "http://test.host/http_digest_authentication_test/dummy_digest", :username => 'pretty', :password => 'please') - @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest/display" + + get :display + + assert_response :success + assert assigns(:logged_in) + assert_equal 'Definitely Maybe', @response.body + end + + test "authentication request with absolute uri in both request and credentials (as in Webrick with IE)" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:url => "http://test.host/http_digest_authentication_test/dummy_digest", + :username => 'pretty', :password => 'please') + @request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest" + get :display assert_response :success @@ -199,11 +222,11 @@ class HttpDigestAuthenticationTest < ActionController::TestCase credentials = decode_credentials(@response.headers['WWW-Authenticate']) credentials.merge!(options) - credentials.reverse_merge!(:uri => "#{@request.env['REQUEST_URI']}") + credentials.merge!(:uri => @request.env['REQUEST_URI'].to_s) ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1]) end def decode_credentials(header) ActionController::HttpAuthentication::Digest.decode_credentials(@response.headers['WWW-Authenticate']) end -end \ No newline at end of file +end -- 1.6.4.2 From 4c96030d05b877919dfe2613290826beee464240 Mon Sep 17 00:00:00 2001 From: Michael Siebert Date: Mon, 6 Apr 2009 20:18:58 +0200 Subject: [PATCH 191/779] Fix deprecating =-methods by using send [#2431 status:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/deprecation.rb | 18 +++++++++--------- activesupport/test/deprecation_test.rb | 4 ++++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index d201516..540be29 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -90,15 +90,15 @@ module ActiveSupport method_names.each do |method_name| alias_method_chain(method_name, :deprecation) do |target, punctuation| class_eval(<<-EOS, __FILE__, __LINE__) - def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block) - ::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn( - self.class.deprecated_method_warning( # self.class.deprecated_method_warning( - :#{method_name}, # :generate_secret, - #{options[method_name].inspect}), # "You should use ActiveSupport::SecureRandom.hex(64)"), - caller # caller - ) # ) - #{target}_without_deprecation#{punctuation}(*args, &block) # generate_secret_without_deprecation(*args, &block) - end # end + def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block) + ::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn( + self.class.deprecated_method_warning( # self.class.deprecated_method_warning( + :#{method_name}, # :generate_secret, + #{options[method_name].inspect}), # "You should use ActiveSupport::SecureRandom.hex(64)"), + caller # caller + ) # ) + send(:#{target}_without_deprecation#{punctuation}, *args, &block) # send(:generate_secret_without_deprecation, *args, &block) + end # end EOS end end diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index 73a1f99..a3ae39d 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -25,6 +25,9 @@ class Deprecatee def e; end deprecate :a, :b, :c => :e, :d => "you now need to do something extra for this one" + def f=(v); end + deprecate :f= + module B C = 1 end @@ -133,6 +136,7 @@ class DeprecationTest < ActiveSupport::TestCase def test_deprecation_without_explanation assert_deprecated { @dtc.a } assert_deprecated { @dtc.b } + assert_deprecated { @dtc.f = :foo } end def test_deprecation_with_alternate_method -- 1.6.4.2 From 5f6623b1b40e2299cdc3f4d5c398fc275d0feaa0 Mon Sep 17 00:00:00 2001 From: Hugo Peixoto Date: Sun, 9 Aug 2009 12:22:04 +0100 Subject: [PATCH 192/779] added tests for namespaced models generation and fixed a bug related to it. Also fixed a pluralization=false issue. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/lib/rails_generator/base.rb | 2 +- .../generators/components/model/model_generator.rb | 11 ++++- railties/test/generators/generator_test_helper.rb | 7 +++ .../test/generators/rails_model_generator_test.rb | 48 ++++++++++++++++++++ 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails_generator/base.rb b/railties/lib/rails_generator/base.rb index aa7081f..dd75f05 100644 --- a/railties/lib/rails_generator/base.rb +++ b/railties/lib/rails_generator/base.rb @@ -233,13 +233,13 @@ module Rails base_name, @class_path, @file_path, @class_nesting, @class_nesting_depth = extract_modules(@name) @class_name_without_nesting, @singular_name, @plural_name = inflect_names(base_name) @table_name = (!defined?(ActiveRecord::Base) || ActiveRecord::Base.pluralize_table_names) ? plural_name : singular_name - @table_name.gsub! '/', '_' if @class_nesting.empty? @class_name = @class_name_without_nesting else @table_name = @class_nesting.underscore << "_" << @table_name @class_name = "#{@class_nesting}::#{@class_name_without_nesting}" end + @table_name.gsub! '/', '_' end # Extract modules from filesystem-style or ruby-style path: diff --git a/railties/lib/rails_generator/generators/components/model/model_generator.rb b/railties/lib/rails_generator/generators/components/model/model_generator.rb index 582a289..1895d8a 100644 --- a/railties/lib/rails_generator/generators/components/model/model_generator.rb +++ b/railties/lib/rails_generator/generators/components/model/model_generator.rb @@ -19,10 +19,17 @@ class ModelGenerator < Rails::Generator::NamedBase m.template 'fixtures.yml', File.join('test/fixtures', "#{table_name}.yml") end + migration_file_path = file_path.gsub(/\//, '_') + migration_name = class_name + if ActiveRecord::Base.pluralize_table_names + migration_name = migration_name.pluralize + migration_file_path = migration_file_path.pluralize + end + unless options[:skip_migration] m.migration_template 'migration.rb', 'db/migrate', :assigns => { - :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}" - }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}" + :migration_name => "Create#{migration_name.gsub(/::/, '')}" + }, :migration_file_name => "create_#{migration_file_path}" end end end diff --git a/railties/test/generators/generator_test_helper.rb b/railties/test/generators/generator_test_helper.rb index 01bf1c9..6f02eb3 100644 --- a/railties/test/generators/generator_test_helper.rb +++ b/railties/test/generators/generator_test_helper.rb @@ -264,6 +264,8 @@ class GeneratorTestCase < Test::Unit::TestCase def assert_generated_migration(name, parent = "ActiveRecord::Migration") file = Dir.glob("#{RAILS_ROOT}/db/migrate/*_#{name.to_s.underscore}.rb").first + assert !file.nil?, "should have generated the migration file but didn't" + file = file.match(/db\/migrate\/[0-9]+_\w+/).to_s assert_generated_class file, parent do |body| assert_match /timestamps/, body, "should have timestamps defined" @@ -300,4 +302,9 @@ class GeneratorTestCase < Test::Unit::TestCase def assert_generated_column(body, name, type) assert_match /t\.#{type.to_s} :#{name.to_s}/, body, "should have column #{name.to_s} defined" end + + # Asserts that the given table is defined in the migration. + def assert_generated_table(body, name) + assert_match /create_table :#{name.to_s} do/, body, "should have table #{name.to_s} defined" + end end diff --git a/railties/test/generators/rails_model_generator_test.rb b/railties/test/generators/rails_model_generator_test.rb index aea2aba..208b0c4 100644 --- a/railties/test/generators/rails_model_generator_test.rb +++ b/railties/test/generators/rails_model_generator_test.rb @@ -45,4 +45,52 @@ class RailsModelGeneratorTest < GeneratorTestCase assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'" end end + + def test_migration_with_namespace + run_generator('model', %w(Gallery::Image)) + assert_generated_migration :gallery_images + assert_skipped_migration :create_images + end + + def test_migration_with_nested_namespace + run_generator('model', %w(Admin::Gallery::Image)) + assert_skipped_migration :create_images + assert_skipped_migration :create_gallery_images + + assert_generated_migration :admin_gallery_images do |t| + assert_generated_table t, :admin_gallery_images + end + end + + def test_migration_with_nested_namespace_without_pluralization + ActiveRecord::Base.pluralize_table_names = false + run_generator('model', %w(Admin::Gallery::Image)) + assert_skipped_migration :create_images + assert_skipped_migration :create_gallery_images + assert_skipped_migration :create_admin_gallery_images + assert_generated_migration :admin_gallery_image do |t| + assert_generated_table t, :admin_gallery_image + end + ensure + ActiveRecord::Base.pluralize_table_names = true + end + + def test_migration_with_namespaces_in_model_name_without_plurization + ActiveRecord::Base.pluralize_table_names = false + run_generator('model', %w(Gallery::Image)) + assert_generated_migration :create_gallery_image + assert_skipped_migration :create_gallery_images + ensure + ActiveRecord::Base.pluralize_table_names = true + end + + def test_migration_without_pluralization + ActiveRecord::Base.pluralize_table_names = false + run_generator('model', %w(Account)) + assert_generated_migration :create_account + assert_skipped_migration :create_accounts + ensure + ActiveRecord::Base.pluralize_table_names = true + end + end -- 1.6.4.2 From 5e4b9469279ea798470ae33c5c0c4cdc503bada6 Mon Sep 17 00:00:00 2001 From: Hugo Peixoto Date: Sun, 9 Aug 2009 15:05:43 +0100 Subject: [PATCH 193/779] Fixed the end_of_* to work with Time.usec (and Time.nsec in ruby1.9) [#1225 status:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_support/core_ext/time/calculations.rb | 14 +++--- activesupport/test/core_ext/date_ext_test.rb | 2 +- activesupport/test/core_ext/time_ext_test.rb | 46 ++++++++++---------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 5ed750a..dfc48f4 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -207,9 +207,9 @@ module ActiveSupport #:nodoc: alias :at_midnight :beginning_of_day alias :at_beginning_of_day :beginning_of_day - # Returns a new Time representing the end of the day (23:59:59) + # Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9) def end_of_day - change(:hour => 23, :min => 59, :sec => 59) + change(:hour => 23, :min => 59, :sec => 59, :usec => 999999.999) end # Returns a new Time representing the start of the month (1st of the month, 0:00) @@ -219,11 +219,11 @@ module ActiveSupport #:nodoc: end alias :at_beginning_of_month :beginning_of_month - # Returns a new Time representing the end of the month (last day of the month, 0:00) + # Returns a new Time representing the end of the month (end of the last day of the month) def end_of_month #self - ((self.mday-1).days + self.seconds_since_midnight) last_day = ::Time.days_in_month( self.month, self.year ) - change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 0) + change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999) end alias :at_end_of_month :end_of_month @@ -233,7 +233,7 @@ module ActiveSupport #:nodoc: end alias :at_beginning_of_quarter :beginning_of_quarter - # Returns a new Time representing the end of the quarter (last day of march, june, september, december, 23:59:59) + # Returns a new Time representing the end of the quarter (end of the last day of march, june, september, december) def end_of_quarter beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month end @@ -245,9 +245,9 @@ module ActiveSupport #:nodoc: end alias :at_beginning_of_year :beginning_of_year - # Returns a new Time representing the end of the year (31st of december, 23:59:59) + # Returns a new Time representing the end of the year (end of the 31st of december) def end_of_year - change(:month => 12,:day => 31,:hour => 23, :min => 59, :sec => 59) + change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999) end alias :at_end_of_year :end_of_year diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 1001868..3a25a85 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -195,7 +195,7 @@ class DateExtCalculationsTest < Test::Unit::TestCase end def test_end_of_day - assert_equal Time.local(2005,2,21,23,59,59), Date.new(2005,2,21).end_of_day + assert_equal Time.local(2005,2,21,23,59,59,999999.999), Date.new(2005,2,21).end_of_day end def test_xmlschema diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index c085552..62c2772 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -84,45 +84,45 @@ class TimeExtCalculationsTest < Test::Unit::TestCase end def test_end_of_day - assert_equal Time.local(2007,8,12,23,59,59), Time.local(2007,8,12,10,10,10).end_of_day + assert_equal Time.local(2007,8,12,23,59,59,999999.999), Time.local(2007,8,12,10,10,10).end_of_day with_env_tz 'US/Eastern' do - assert_equal Time.local(2007,4,2,23,59,59), Time.local(2007,4,2,10,10,10).end_of_day, 'start DST' - assert_equal Time.local(2007,10,29,23,59,59), Time.local(2007,10,29,10,10,10).end_of_day, 'ends DST' + assert_equal Time.local(2007,4,2,23,59,59,999999.999), Time.local(2007,4,2,10,10,10).end_of_day, 'start DST' + assert_equal Time.local(2007,10,29,23,59,59,999999.999), Time.local(2007,10,29,10,10,10).end_of_day, 'ends DST' end with_env_tz 'NZ' do - assert_equal Time.local(2006,3,19,23,59,59), Time.local(2006,3,19,10,10,10).end_of_day, 'ends DST' - assert_equal Time.local(2006,10,1,23,59,59), Time.local(2006,10,1,10,10,10).end_of_day, 'start DST' + assert_equal Time.local(2006,3,19,23,59,59,999999.999), Time.local(2006,3,19,10,10,10).end_of_day, 'ends DST' + assert_equal Time.local(2006,10,1,23,59,59,999999.999), Time.local(2006,10,1,10,10,10).end_of_day, 'start DST' end end def test_end_of_week - assert_equal Time.local(2008,1,6,23,59,59), Time.local(2007,12,31,10,10,10).end_of_week - assert_equal Time.local(2007,9,2,23,59,59), Time.local(2007,8,27,0,0,0).end_of_week #monday - assert_equal Time.local(2007,9,2,23,59,59), Time.local(2007,8,28,0,0,0).end_of_week #tuesday - assert_equal Time.local(2007,9,2,23,59,59), Time.local(2007,8,29,0,0,0).end_of_week #wednesday - assert_equal Time.local(2007,9,2,23,59,59), Time.local(2007,8,30,0,0,0).end_of_week #thursday - assert_equal Time.local(2007,9,2,23,59,59), Time.local(2007,8,31,0,0,0).end_of_week #friday - assert_equal Time.local(2007,9,2,23,59,59), Time.local(2007,9,01,0,0,0).end_of_week #saturday - assert_equal Time.local(2007,9,2,23,59,59), Time.local(2007,9,02,0,0,0).end_of_week #sunday + assert_equal Time.local(2008,1,6,23,59,59,999999.999), Time.local(2007,12,31,10,10,10).end_of_week + assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,27,0,0,0).end_of_week #monday + assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,28,0,0,0).end_of_week #tuesday + assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,29,0,0,0).end_of_week #wednesday + assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,30,0,0,0).end_of_week #thursday + assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,31,0,0,0).end_of_week #friday + assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,9,01,0,0,0).end_of_week #saturday + assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,9,02,0,0,0).end_of_week #sunday end def test_end_of_month - assert_equal Time.local(2005,3,31,23,59,59), Time.local(2005,3,20,10,10,10).end_of_month - assert_equal Time.local(2005,2,28,23,59,59), Time.local(2005,2,20,10,10,10).end_of_month - assert_equal Time.local(2005,4,30,23,59,59), Time.local(2005,4,20,10,10,10).end_of_month + assert_equal Time.local(2005,3,31,23,59,59,999999.999), Time.local(2005,3,20,10,10,10).end_of_month + assert_equal Time.local(2005,2,28,23,59,59,999999.999), Time.local(2005,2,20,10,10,10).end_of_month + assert_equal Time.local(2005,4,30,23,59,59,999999.999), Time.local(2005,4,20,10,10,10).end_of_month end def test_end_of_quarter - assert_equal Time.local(2007,3,31,23,59,59), Time.local(2007,2,15,10,10,10).end_of_quarter - assert_equal Time.local(2007,3,31,23,59,59), Time.local(2007,3,31,0,0,0).end_of_quarter - assert_equal Time.local(2007,12,31,23,59,59), Time.local(2007,12,21,10,10,10).end_of_quarter - assert_equal Time.local(2007,6,30,23,59,59), Time.local(2007,4,1,0,0,0).end_of_quarter - assert_equal Time.local(2008,6,30,23,59,59), Time.local(2008,5,31,0,0,0).end_of_quarter + assert_equal Time.local(2007,3,31,23,59,59,999999.999), Time.local(2007,2,15,10,10,10).end_of_quarter + assert_equal Time.local(2007,3,31,23,59,59,999999.999), Time.local(2007,3,31,0,0,0).end_of_quarter + assert_equal Time.local(2007,12,31,23,59,59,999999.999), Time.local(2007,12,21,10,10,10).end_of_quarter + assert_equal Time.local(2007,6,30,23,59,59,999999.999), Time.local(2007,4,1,0,0,0).end_of_quarter + assert_equal Time.local(2008,6,30,23,59,59,999999.999), Time.local(2008,5,31,0,0,0).end_of_quarter end def test_end_of_year - assert_equal Time.local(2007,12,31,23,59,59), Time.local(2007,2,22,10,10,10).end_of_year - assert_equal Time.local(2007,12,31,23,59,59), Time.local(2007,12,31,10,10,10).end_of_year + assert_equal Time.local(2007,12,31,23,59,59,999999.999), Time.local(2007,2,22,10,10,10).end_of_year + assert_equal Time.local(2007,12,31,23,59,59,999999.999), Time.local(2007,12,31,10,10,10).end_of_year end def test_beginning_of_year -- 1.6.4.2 From 8056c57a94a2cbbbeb662ebe5b0cc6aa0ca1ef00 Mon Sep 17 00:00:00 2001 From: Mike Breen Date: Sun, 9 Aug 2009 11:10:59 -0400 Subject: [PATCH 194/779] Serialized attributes should only be saved with partial_updates when the serialized attribute is present [#2397 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/dirty.rb | 2 +- activerecord/test/cases/dirty_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index 4a2510a..f189651 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -143,7 +143,7 @@ module ActiveRecord if partial_updates? # Serialized attributes should always be written in case they've been # changed in place. - update_without_dirty(changed | self.class.serialized_attributes.keys) + update_without_dirty(changed | (attributes.keys & self.class.serialized_attributes.keys)) else update_without_dirty end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index ac95bac..fb3d26f 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -288,6 +288,16 @@ class DirtyTest < ActiveRecord::TestCase end end + def test_save_should_not_save_serialized_attribute_with_partial_updates_if_not_present + with_partial_updates(Topic) do + Topic.create!(:author_name => 'Bill', :content => {:a => "a"}) + topic = Topic.first(:select => 'id, author_name') + topic.update_attribute :author_name, 'John' + topic = Topic.first + assert_not_nil topic.content + end + end + private def with_partial_updates(klass, on = true) old = klass.partial_updates? -- 1.6.4.2 From d1202cfeb2cc7961c93a33ef3f5622d5393186f1 Mon Sep 17 00:00:00 2001 From: rizwanreza Date: Sun, 9 Aug 2009 05:55:25 +0300 Subject: [PATCH 195/779] Support passing Redcloth options via textilize helper [#2973 state:resolved] Signed-off-by: Pratik Naik --- actionpack/lib/action_view/helpers/text_helper.rb | 14 +++++++++++--- actionpack/test/template/text_helper_test.rb | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 8f1ddb9..de6a2dd 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -235,12 +235,20 @@ module ActionView # # textilize("Visit the Rails website "here":http://www.rubyonrails.org/.) # # => "

Visit the Rails website here.

" - def textilize(text) + # + # textilize("This is worded strongly") + # # => "

This is worded strongly

" + # + # textilize("This is worded strongly", :filter_html) + # # => "

This is worded <strong>strongly</strong>

" + # + def textilize(text, *options) + options ||= [:hard_breaks] + if text.blank? "" else - textilized = RedCloth.new(text, [ :hard_breaks ]) - textilized.hard_breaks = true if textilized.respond_to?(:hard_breaks=) + textilized = RedCloth.new(text, options) textilized.to_html end end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index a370f14..4ca8403 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'testing_sandbox' +require 'redcloth' class TextHelperTest < ActionView::TestCase tests ActionView::Helpers::TextHelper @@ -517,4 +518,20 @@ class TextHelperTest < ActionView::TestCase assert_equal("red", cycle("red", "blue")) assert_equal(%w{Specialized Fuji Giant}, @cycles) end + + def test_textilize + assert_equal("

This is Textile! Rejoice!

", textilize("*This is Textile!* Rejoice!")) + end + + def test_textilize_with_blank + assert_equal("", textilize("")) + end + + def test_textilize_with_options + assert_equal("

This is worded <strong>strongly</strong>

", textilize("This is worded strongly", :filter_html)) + end + + def test_textilize_with_hard_breaks + assert_equal("

This is one scary world.
\n True.

", textilize("This is one scary world.\n True.")) + end end -- 1.6.4.2 From 80d8608102cce64805e7573f6b97d8561f7f11ea Mon Sep 17 00:00:00 2001 From: railsbob Date: Sun, 9 Aug 2009 13:01:42 +0100 Subject: [PATCH 196/779] Ensure hm:t#find does not assign nil to :include [#1845 state:resolved] Signed-off-by: Pratik Naik --- .../associations/has_many_through_association.rb | 2 +- .../has_many_through_associations_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletions(-) 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 1c091e7..ac789e2 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -44,7 +44,7 @@ module ActiveRecord options[:select] = construct_select(options[:select]) options[:from] ||= construct_from options[:joins] = construct_joins(options[:joins]) - options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil? + options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil? && @reflection.source_reflection.options[:include] end def insert_record(record, force = true, validate = 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 97efca7..a8cdfc5 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -276,4 +276,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_has_many_association_through_a_has_many_association_with_nonstandard_primary_keys assert_equal 1, owners(:blackbeard).toys.count end + + def test_find_on_has_many_association_collection_with_include_and_conditions + post_with_no_comments = people(:michael).posts_with_no_comments.first + assert_equal post_with_no_comments, posts(:authorless) + end end -- 1.6.4.2 From d3a802cee0f8cbe209af8886f8f8e8cab204d21a Mon Sep 17 00:00:00 2001 From: Matt Conway Date: Wed, 22 Apr 2009 22:25:46 -0400 Subject: [PATCH 197/779] Allow connect_timeout, read_timeout and write_timeout settings for MySQL [#2544 state:resolved] Signed-off-by: Pratik Naik --- .../connection_adapters/mysql_adapter.rb | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 9300df2..4542fe6 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -573,6 +573,10 @@ module ActiveRecord @connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher]) end + @connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout] + @connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout] + @connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout] + @connection.real_connect(*@connection_options) # reconnect must be set after real_connect is called, because real_connect sets it to false internally -- 1.6.4.2 From 8058a1d7d7e2b66d0275bf852712f78394c19d01 Mon Sep 17 00:00:00 2001 From: Dan Croak Date: Sun, 9 Aug 2009 12:54:51 -0400 Subject: [PATCH 198/779] Deprecation warning for routes_for_controller_and_action. Use routes_for instead. [#3023] Signed-off-by: Pratik Naik --- .../lib/action_controller/routing/route_set.rb | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index a983d37..8c4a257 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -464,6 +464,7 @@ module ActionController end def routes_for_controller_and_action(controller, action) + ActionSupport::Deprecation.warn "routes_for_controller_and_action() has been deprecated. Please use routes_for()" selected = routes.select do |route| route.matches_controller_and_action? controller, action end -- 1.6.4.2 From c41fb5865f342fd88df45a9a920ac582a17f314d Mon Sep 17 00:00:00 2001 From: Simon Jefford Date: Sun, 9 Aug 2009 18:26:04 +0100 Subject: [PATCH 199/779] Add test for routes_for_controller_and_action deprecation [#3023] Signed-off-by: Pratik Naik --- .../lib/action_controller/routing/route_set.rb | 2 +- actionpack/test/controller/routing_test.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 8c4a257..8fdf64c 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -464,7 +464,7 @@ module ActionController end def routes_for_controller_and_action(controller, action) - ActionSupport::Deprecation.warn "routes_for_controller_and_action() has been deprecated. Please use routes_for()" + ActiveSupport::Deprecation.warn "routes_for_controller_and_action() has been deprecated. Please use routes_for()" selected = routes.select do |route| route.matches_controller_and_action? controller, action end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 9375942..f10e5a9 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 'action_controller/routing/route_set' class MilestonesController < ActionController::Base def index() head :ok end @@ -742,7 +743,7 @@ class MockController end end -class LegacyRouteSetTests < Test::Unit::TestCase +class LegacyRouteSetTests < ActiveSupport::TestCase attr_reader :rs def setup @@ -758,6 +759,10 @@ class LegacyRouteSetTests < Test::Unit::TestCase @rs.clear! end + def test_routes_for_controller_and_action_deprecated + assert_deprecated { @rs.routes_for_controller_and_action("controller", "action") } + end + def test_default_setup @rs.draw {|m| m.connect ':controller/:action/:id' } assert_equal({:controller => "content", :action => 'index'}, rs.recognize_path("/content")) -- 1.6.4.2 From d44b5c6219bdf5c8d80fe2f2006541d22227a8f5 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 9 Aug 2009 11:02:34 -0700 Subject: [PATCH 200/779] Fix that RedCloth shouldn't be required to run tests --- actionpack/test/template/text_helper_test.rb | 30 +++++++++++++++---------- 1 files changed, 18 insertions(+), 12 deletions(-) diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 4ca8403..715c390 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -1,6 +1,10 @@ require 'abstract_unit' require 'testing_sandbox' -require 'redcloth' +begin + require 'redcloth' +rescue LoadError + $stderr.puts "Skipping textilize tests. `gem install RedCloth` to enable." +end class TextHelperTest < ActionView::TestCase tests ActionView::Helpers::TextHelper @@ -519,19 +523,21 @@ class TextHelperTest < ActionView::TestCase assert_equal(%w{Specialized Fuji Giant}, @cycles) end - def test_textilize - assert_equal("

This is Textile! Rejoice!

", textilize("*This is Textile!* Rejoice!")) - end + if defined? RedCloth + def test_textilize + assert_equal("

This is Textile! Rejoice!

", textilize("*This is Textile!* Rejoice!")) + end - def test_textilize_with_blank - assert_equal("", textilize("")) - end + def test_textilize_with_blank + assert_equal("", textilize("")) + end - def test_textilize_with_options - assert_equal("

This is worded <strong>strongly</strong>

", textilize("This is worded strongly", :filter_html)) - end + def test_textilize_with_options + assert_equal("

This is worded <strong>strongly</strong>

", textilize("This is worded strongly", :filter_html)) + end - def test_textilize_with_hard_breaks - assert_equal("

This is one scary world.
\n True.

", textilize("This is one scary world.\n True.")) + def test_textilize_with_hard_breaks + assert_equal("

This is one scary world.
\n True.

", textilize("This is one scary world.\n True.")) + end end end -- 1.6.4.2 From a8286af3c335eeb80245db2ffbcfc6c06fb6fc60 Mon Sep 17 00:00:00 2001 From: Hugo Peixoto Date: Sat, 8 Aug 2009 23:22:22 +0100 Subject: [PATCH 201/779] Added a uniqueness validation test that uses diacritics. [#2883 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/test/cases/validations_test.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index c20f5ae..af2ce87 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -345,13 +345,13 @@ class ValidationsTest < ActiveRecord::TestCase def test_validate_uniqueness Topic.validates_uniqueness_of(:title) - t = Topic.new("title" => "I'm unique!") + t = Topic.new("title" => "I'm uniqué!") assert t.save, "Should save t as unique" t.content = "Remaining unique" assert t.save, "Should still save t as unique" - t2 = Topic.new("title" => "I'm unique!") + t2 = Topic.new("title" => "I'm uniqué!") assert !t2.valid?, "Shouldn't be valid" assert !t2.save, "Shouldn't save t2 as unique" assert_equal "has already been taken", t2.errors.on(:title) -- 1.6.4.2 From f6f04f1549cea33385323b6a1fd2c58855927d15 Mon Sep 17 00:00:00 2001 From: Elise Huard Date: Sun, 9 Aug 2009 10:24:28 +0200 Subject: [PATCH 202/779] validate uniqueness with limit in utf8 [#2653 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/validations.rb | 2 +- activerecord/test/cases/validations_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index d2d12b8..7a7d0ab 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -726,7 +726,7 @@ module ActiveRecord comparison_operator = "IS ?" elsif column.text? comparison_operator = "#{connection.case_sensitive_equality_operator} ?" - value = column.limit ? value.to_s[0, column.limit] : value.to_s + value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s else comparison_operator = "= ?" end diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index af2ce87..4c023c3 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -539,6 +539,16 @@ class ValidationsTest < ActiveRecord::TestCase assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique" end + def test_validate_uniqueness_with_limit_and_utf8 + with_kcode('UTF8') do + # Event.title is limited to 5 characters + e1 = Event.create(:title => "一二三四五") + assert e1.valid?, "Could not create an event with a unique, 5 character title" + e2 = Event.create(:title => "一二三四五六七八") + assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique" + end + end + def test_validate_straight_inheritance_uniqueness w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork") assert w1.valid?, "Saving w1" -- 1.6.4.2 From de0b073f3e7db069e3a572f142ce7a5241b102c2 Mon Sep 17 00:00:00 2001 From: Roy Nicholson Date: Sun, 9 Aug 2009 13:57:20 -0400 Subject: [PATCH 203/779] Add ability to set SSL options on ARes connections. [#2370 state:committed] Signed-off-by: Jeremy Kemper --- activeresource/CHANGELOG | 2 + activeresource/lib/active_resource/base.rb | 41 +++++++++++++++++ activeresource/lib/active_resource/connection.rb | 43 +++++++++++++++++- activeresource/test/base_test.rb | 51 ++++++++++++++++++++++ activeresource/test/connection_test.rb | 18 ++++++++ 5 files changed, 152 insertions(+), 3 deletions(-) diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index 00f4bd1..caabb7d 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,5 +1,7 @@ *2.3.4 (pending)* +* More thorough SSL support. #2370 [Roy Nicholson] + * HTTP proxy support. #2133 [Marshall Huss, Sébastien Dabet] diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index aa6f599..d9d1016 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -93,6 +93,8 @@ module ActiveResource # # Many REST APIs will require authentication, usually in the form of basic # HTTP authentication. Authentication can be specified by: + # + # === HTTP Basic Authentication # * putting the credentials in the URL for the +site+ variable. # # class Person < ActiveResource::Base @@ -112,6 +114,19 @@ module ActiveResource # # Note: Some values cannot be provided in the URL passed to site. e.g. email addresses # as usernames. In those situations you should use the separate user and password option. + # + # === Certificate Authentication + # + # * End point uses an X509 certificate for authentication. See ssl_options= for all options. + # + # class Person < ActiveResource::Base + # self.site = "https://secure.api.people.com/" + # self.ssl_options = {:cert => OpenSSL::X509::Certificate.new(File.open(pem_file)) + # :key => OpenSSL::PKey::RSA.new(File.open(pem_file)), + # :ca_path => "/path/to/OpenSSL/formatted/CA_Certs", + # :verify_mode => OpenSSL::SSL::VERIFY_PEER} + # end + # # == Errors & Validation # # Error handling and validation is handled in much the same manner as you're used to seeing in @@ -331,6 +346,31 @@ module ActiveResource end end + # Options that will get applied to an SSL connection. + # + # * :key - An OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. + # * :cert - An OpenSSL::X509::Certificate object as client certificate + # * :ca_file - Path to a CA certification file in PEM format. The file can contrain several CA certificates. + # * :ca_path - Path of a CA certification directory containing certifications in PEM format. + # * :verify_mode - Flags for server the certification verification at begining of SSL/TLS session. (OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER is acceptable) + # * :verify_callback - The verify callback for the server certification verification. + # * :verify_depth - The maximum depth for the certificate chain verification. + # * :cert_store - OpenSSL::X509::Store to verify peer certificate. + # * :ssl_timeout -The SSL timeout in seconds. + def ssl_options=(opts={}) + @connection = nil + @ssl_options = opts + end + + # Returns the SSL options hash. + def ssl_options + if defined?(@ssl_options) + @ssl_options + elsif superclass != Object && superclass.ssl_options + superclass.ssl_options + end + end + # An instance of ActiveResource::Connection that is the base \connection to the remote service. # The +refresh+ parameter toggles whether or not the \connection is refreshed at every request # or not (defaults to false). @@ -341,6 +381,7 @@ module ActiveResource @connection.user = user if user @connection.password = password if password @connection.timeout = timeout if timeout + @connection.ssl_options = ssl_options if ssl_options @connection else superclass.connection diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb index 48b44b3..01bcbe7 100644 --- a/activeresource/lib/active_resource/connection.rb +++ b/activeresource/lib/active_resource/connection.rb @@ -26,6 +26,14 @@ module ActiveResource def to_s; @message ;end end + # Raised when a OpenSSL::SSL::SSLError occurs. + class SSLError < ConnectionError + def initialize(message) + @message = message + end + def to_s; @message ;end + end + # 3xx Redirection class Redirection < ConnectionError # :nodoc: def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end @@ -70,7 +78,7 @@ module ActiveResource :delete => 'Accept' } - attr_reader :site, :user, :password, :timeout, :proxy + attr_reader :site, :user, :password, :timeout, :proxy, :ssl_options attr_accessor :format class << self @@ -115,6 +123,11 @@ module ActiveResource @timeout = timeout end + # Hash of options applied to Net::HTTP instance when +site+ protocol is 'https'. + def ssl_options=(opts={}) + @ssl_options = opts + end + # Execute a GET request. # Used to get (find) resources. def get(path, headers = {}) @@ -156,6 +169,8 @@ module ActiveResource handle_response(result) rescue Timeout::Error => e raise TimeoutError.new(e.message) + rescue OpenSSL::SSL::SSLError => e + raise SSLError.new(e.message) end # Handles response and error codes from remote service. @@ -203,8 +218,7 @@ module ActiveResource end def configure_http(http) - http.use_ssl = @site.is_a?(URI::HTTPS) - http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl? + http = apply_ssl_options(http) # Net::HTTP timeouts default to 60 seconds. if @timeout @@ -215,6 +229,29 @@ module ActiveResource http end + def apply_ssl_options(http) + return http unless @site.is_a?(URI::HTTPS) + + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + return http unless defined?(@ssl_options) + + http.ca_path = @ssl_options[:ca_path] if @ssl_options[:ca_path] + http.ca_file = @ssl_options[:ca_file] if @ssl_options[:ca_file] + + http.cert = @ssl_options[:cert] if @ssl_options[:cert] + http.key = @ssl_options[:key] if @ssl_options[:key] + + http.cert_store = @ssl_options[:cert_store] if @ssl_options[:cert_store] + http.ssl_timeout = @ssl_options[:ssl_timeout] if @ssl_options[:ssl_timeout] + + http.verify_mode = @ssl_options[:verify_mode] if @ssl_options[:verify_mode] + http.verify_callback = @ssl_options[:verify_callback] if @ssl_options[:verify_callback] + http.verify_depth = @ssl_options[:verify_depth] if @ssl_options[:verify_depth] + + http + end + def default_header @default_header ||= {} end diff --git a/activeresource/test/base_test.rb b/activeresource/test/base_test.rb index a263153..26020bd 100644 --- a/activeresource/test/base_test.rb +++ b/activeresource/test/base_test.rb @@ -165,6 +165,13 @@ class BaseTest < Test::Unit::TestCase assert_equal(5, Forum.connection.timeout) end + def test_should_accept_setting_ssl_options + expected = {:verify => 1} + Forum.ssl_options= expected + assert_equal(expected, Forum.ssl_options) + assert_equal(expected, Forum.connection.ssl_options) + end + def test_user_variable_can_be_reset actor = Class.new(ActiveResource::Base) actor.site = 'http://cinema' @@ -195,6 +202,16 @@ class BaseTest < Test::Unit::TestCase assert_nil actor.connection.timeout end + def test_ssl_options_hash_can_be_reset + actor = Class.new(ActiveResource::Base) + actor.site = 'https://cinema' + assert_nil actor.ssl_options + actor.ssl_options = {:foo => 5} + actor.ssl_options = nil + assert_nil actor.ssl_options + assert_nil actor.connection.ssl_options + end + def test_credentials_from_site_are_decoded actor = Class.new(ActiveResource::Base) actor.site = 'http://my%40email.com:%31%32%33@cinema' @@ -394,6 +411,40 @@ class BaseTest < Test::Unit::TestCase assert_equal fruit.timeout, apple.timeout, 'subclass did not adopt changes from parent class' end + def test_ssl_options_reader_uses_superclass_ssl_options_until_written + # Superclass is Object so returns nil. + assert_nil ActiveResource::Base.ssl_options + assert_nil Class.new(ActiveResource::Base).ssl_options + Person.ssl_options = {:foo => 'bar'} + + # Subclass uses superclass ssl_options. + actor = Class.new(Person) + assert_equal Person.ssl_options, actor.ssl_options + + # Changing subclass ssl_options doesn't change superclass ssl_options. + actor.ssl_options = {:baz => ''} + assert_not_equal Person.ssl_options, actor.ssl_options + + # Changing superclass ssl_options doesn't overwrite subclass ssl_options. + Person.ssl_options = {:color => 'blue'} + assert_not_equal Person.ssl_options, actor.ssl_options + + # Changing superclass ssl_options after subclassing changes subclass ssl_options. + jester = Class.new(actor) + actor.ssl_options = {:color => 'red'} + assert_equal actor.ssl_options, jester.ssl_options + + # Subclasses are always equal to superclass ssl_options when not overridden. + fruit = Class.new(ActiveResource::Base) + apple = Class.new(fruit) + + fruit.ssl_options = {:alpha => 'betas'} + assert_equal fruit.ssl_options, apple.ssl_options, 'subclass did not adopt changes from parent class' + + fruit.ssl_options = {:omega => 'moos'} + assert_equal fruit.ssl_options, apple.ssl_options, 'subclass did not adopt changes from parent class' + end + def test_updating_baseclass_site_object_wipes_descendent_cached_connection_objects # Subclasses are always equal to superclass site when not overridden fruit = Class.new(ActiveResource::Base) diff --git a/activeresource/test/connection_test.rb b/activeresource/test/connection_test.rb index 12e8058..b482f2d 100644 --- a/activeresource/test/connection_test.rb +++ b/activeresource/test/connection_test.rb @@ -204,6 +204,24 @@ class ConnectionTest < Test::Unit::TestCase assert_nothing_raised(Mocha::ExpectationError) { @conn.get(path, {'Accept' => 'application/xhtml+xml'}) } end + def test_ssl_options_get_applied_to_http + http = Net::HTTP.new('') + @conn.site="https://secure" + @conn.ssl_options={:verify_mode => OpenSSL::SSL::VERIFY_PEER} + @conn.timeout = 10 # prevent warning about uninitialized. + @conn.send(:configure_http, http) + + assert http.use_ssl? + assert_equal http.verify_mode, OpenSSL::SSL::VERIFY_PEER + end + + def test_ssl_error + http = Net::HTTP.new('') + @conn.expects(:http).returns(http) + http.expects(:get).raises(OpenSSL::SSL::SSLError, 'Expired certificate') + assert_raise(ActiveResource::SSLError) { @conn.get('/people/1.xml') } + end + protected def assert_response_raises(klass, code) assert_raise(klass, "Expected response code #{code} to raise #{klass}") do -- 1.6.4.2 From 2c4f4a8734b4137adc331186bb2255fb6a38c31e Mon Sep 17 00:00:00 2001 From: Hugo Peixoto Date: Sun, 9 Aug 2009 09:20:18 +0100 Subject: [PATCH 204/779] With multiparameter date attributes, the behaviour when empty fields are present is now coherent with the one described in the date_select documentation. [#1715 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/base.rb | 14 ++++++--- activerecord/test/cases/base_test.rb | 47 +++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d155837..4bb48ee 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -3028,16 +3028,22 @@ module ActiveRecord #:nodoc: def execute_callstack_for_multiparameter_attributes(callstack) errors = [] - callstack.each do |name, values| + callstack.each do |name, values_with_empty_parameters| begin klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass + # in order to allow a date to be set without a year, we must keep the empty values. + # Otherwise, we wouldn't be able to distinguish it from a date with an empty day. + values = values_with_empty_parameters.reject(&:nil?) + if values.empty? send(name + "=", nil) else + value = if Time == klass instantiate_time_object(name, values) elsif Date == klass begin + values = values_with_empty_parameters.collect do |v| v.nil? ? 1 : v end Date.new(*values) rescue ArgumentError => ex # if Date.new raises an exception on an invalid date instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates @@ -3065,10 +3071,8 @@ module ActiveRecord #:nodoc: attribute_name = multiparameter_name.split("(").first attributes[attribute_name] = [] unless attributes.include?(attribute_name) - unless value.empty? - attributes[attribute_name] << - [ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ] - end + parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value) + attributes[attribute_name] << [ find_parameter_position(multiparameter_name), parameter_value ] end attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } } diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index e57e46f..4530eec 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1012,7 +1012,25 @@ class BasicsTest < ActiveRecord::TestCase assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date end - def test_multiparameter_attributes_on_date_with_empty_date + def test_multiparameter_attributes_on_date_with_empty_year + attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(1, 6, 24), topic.last_read.to_date + end + + def test_multiparameter_attributes_on_date_with_empty_month + attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(2004, 1, 24), topic.last_read.to_date + end + + def test_multiparameter_attributes_on_date_with_empty_day attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" } topic = Topic.find(1) topic.attributes = attributes @@ -1021,6 +1039,33 @@ class BasicsTest < ActiveRecord::TestCase assert_date_from_db Date.new(2004, 6, 1), topic.last_read.to_date end + def test_multiparameter_attributes_on_date_with_empty_day_and_year + attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(1, 6, 1), topic.last_read.to_date + end + + def test_multiparameter_attributes_on_date_with_empty_day_and_month + attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(2004, 1, 1), topic.last_read.to_date + end + + def test_multiparameter_attributes_on_date_with_empty_year_and_month + attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" } + topic = Topic.find(1) + topic.attributes = attributes + # note that extra #to_date call allows test to pass for Oracle, which + # treats dates/times the same + assert_date_from_db Date.new(1, 1, 24), topic.last_read.to_date + end + def test_multiparameter_attributes_on_date_with_all_empty attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" } topic = Topic.find(1) -- 1.6.4.2 From c9d4bcf16337241cc270eddb402b17cc094c609e Mon Sep 17 00:00:00 2001 From: David Burger Date: Sun, 9 Aug 2009 14:05:07 -0500 Subject: [PATCH 205/779] Fix that Hash#to_xml and Array#to_xml shouldn't modify their options hashes [#672 state:resolved] [David Burger, Dana Jones] Signed-off-by: Pratik Naik --- .../active_support/core_ext/array/conversions.rb | 1 + .../active_support/core_ext/hash/conversions.rb | 1 + activesupport/test/core_ext/array_ext_test.rb | 7 +++++++ activesupport/test/core_ext/hash_ext_test.rb | 7 +++++++ 4 files changed, 16 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index ba8e022..779743b 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -163,6 +163,7 @@ module ActiveSupport #:nodoc: raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml } require 'builder' unless defined?(Builder) + options = options.dup options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records" options[:children] ||= options[:root].singularize options[:indent] ||= 2 diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 5ae43c0..a43763f 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -99,6 +99,7 @@ module ActiveSupport #:nodoc: def to_xml(options = {}) require 'builder' unless defined?(Builder) + options = options.dup options[:indent] ||= 2 options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]), :root => "hash" }) diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index b70ec47..fe48d39 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -233,6 +233,13 @@ class ArrayToXmlTests < Test::Unit::TestCase assert xml.include?(%(Jason)), xml end + def test_to_xml_dups_options + options = {:skip_instruct => true} + [].to_xml(options) + # :builder, etc, shouldn't be added to options + assert_equal({:skip_instruct => true}, options) + end + def test_to_xml_with_dedicated_name xml = [ { :name => "David", :age => 26, :age_in_millis => 820497600000 }, { :name => "Jason", :age => 31 } diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index c58c0ac..736c1c1 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -899,6 +899,13 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal hash, Hash.from_xml(hash.to_xml(@xml_options))['person'] end + def test_to_xml_dups_options + options = {:skip_instruct => true} + {}.to_xml(options) + # :builder, etc, shouldn't be added to options + assert_equal({:skip_instruct => true}, options) + end + def test_datetime_xml_type_with_utc_time alert_xml = <<-XML -- 1.6.4.2 From 29a5549b3408597f3d3eb17350737631368c6cc9 Mon Sep 17 00:00:00 2001 From: Joshua Nichols Date: Sun, 9 Aug 2009 13:13:34 -0400 Subject: [PATCH 206/779] Added back support for destroying an association's object by id. [#2306 status:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../associations/association_collection.rb | 1 + .../associations/has_many_associations_test.rb | 22 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 0 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 3aef1b2..42b6e5d 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -208,6 +208,7 @@ module ActiveRecord # Note that this method will _always_ remove records from the database # ignoring the +:dependent+ option. def destroy(*records) + records = find(records) if records.any? {|record| record.kind_of?(Fixnum) || record.kind_of?(String)} remove_records(records) do |records, old_records| old_records.each { |record| record.destroy } end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 5df74fc..c78dba1 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -691,6 +691,28 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 0, companies(:first_firm).clients_of_firm(true).size end + def test_destroying_by_fixnum_id + force_signal37_to_load_all_clients_of_firm + + assert_difference "Client.count", -1 do + companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first.id) + end + + assert_equal 0, companies(:first_firm).reload.clients_of_firm.size + assert_equal 0, companies(:first_firm).clients_of_firm(true).size + end + + def test_destroying_by_string_id + force_signal37_to_load_all_clients_of_firm + + assert_difference "Client.count", -1 do + companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first.id.to_s) + end + + assert_equal 0, companies(:first_firm).reload.clients_of_firm.size + assert_equal 0, companies(:first_firm).clients_of_firm(true).size + end + def test_destroying_a_collection force_signal37_to_load_all_clients_of_firm companies(:first_firm).clients_of_firm.create("name" => "Another Client") -- 1.6.4.2 From 323f58f19f052d77e972b94767ab133d58fdcd90 Mon Sep 17 00:00:00 2001 From: Jatinder Singh Date: Sun, 9 Aug 2009 22:24:50 +0100 Subject: [PATCH 207/779] Make ActiveResource#exists? work [#3020 state:resolved] Signed-off-by: Pratik Naik --- activeresource/lib/active_resource/connection.rb | 5 +++-- activeresource/test/base_test.rb | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb index 01bcbe7..acb9ff5 100644 --- a/activeresource/lib/active_resource/connection.rb +++ b/activeresource/lib/active_resource/connection.rb @@ -75,7 +75,8 @@ module ActiveResource HTTP_FORMAT_HEADER_NAMES = { :get => 'Accept', :put => 'Content-Type', :post => 'Content-Type', - :delete => 'Accept' + :delete => 'Accept', + :head => 'Accept' } attr_reader :site, :user, :password, :timeout, :proxy, :ssl_options @@ -155,7 +156,7 @@ module ActiveResource # Execute a HEAD request. # Used to obtain meta-information about resources, such as whether they exist and their size (via response headers). def head(path, headers = {}) - request(:head, path, build_request_headers(headers)) + request(:head, path, build_request_headers(headers, :head)) end diff --git a/activeresource/test/base_test.rb b/activeresource/test/base_test.rb index 26020bd..e725d8e 100644 --- a/activeresource/test/base_test.rb +++ b/activeresource/test/base_test.rb @@ -965,6 +965,14 @@ class BaseTest < Test::Unit::TestCase end end + def test_exists_without_http_mock + http = Net::HTTP.new(Person.site.host, Person.site.port) + ActiveResource::Connection.any_instance.expects(:http).returns(http) + http.expects(:request).returns(ActiveResource::Response.new("")) + + assert Person.exists?('not-mocked') + end + def test_to_xml matz = Person.find(1) xml = matz.encode -- 1.6.4.2 From 250e718355287de23604e8171d66138522eddd63 Mon Sep 17 00:00:00 2001 From: Bence Nagy Date: Sat, 27 Dec 2008 23:28:37 +0100 Subject: [PATCH 208/779] path_names could be used to customize collection actions too Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/resources.rb | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index 86abb7b..06c58fe 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -586,7 +586,10 @@ module ActionController resource.collection_methods.each do |method, actions| actions.each do |action| [method].flatten.each do |m| - map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action}", "#{action}_#{resource.name_prefix}#{resource.plural}", m) + action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash) + action_path ||= action + + map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.name_prefix}#{resource.plural}", m) end end end -- 1.6.4.2 From 39de15f13655ed78ed7149e8eb8721139a57d501 Mon Sep 17 00:00:00 2001 From: Hugo Peixoto Date: Sun, 9 Aug 2009 07:28:29 +0100 Subject: [PATCH 209/779] Added both the documentation and a test case for the collection path name customization feature. [#1218 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/resources.rb | 5 ++- actionpack/test/controller/resources_test.rb | 44 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index 06c58fe..2d3f82a 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -317,9 +317,10 @@ module ActionController # notes.resources :attachments # end # - # * :path_names - Specify different names for the 'new' and 'edit' actions. For example: + # * :path_names - Specify different path names for the actions. For example: # # new_products_path == '/productos/nuevo' - # map.resources :products, :as => 'productos', :path_names => { :new => 'nuevo', :edit => 'editar' } + # # bids_product_path(1) == '/productos/1/licitacoes' + # map.resources :products, :as => 'productos', :member => { :bids => :get }, :path_names => { :new => 'nuevo', :bids => 'licitacoes' } # # You can also set default action names from an environment, like this: # config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' } diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 30ab110..0b639e3 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -76,6 +76,50 @@ class ResourcesTest < ActionController::TestCase end end + def test_override_paths_for_member_and_collection_methods + collection_methods = { 'rss' => :get, 'reorder' => :post, 'csv' => :post } + member_methods = { 'rss' => :get, :atom => :get, :upload => :post, :fix => :post } + path_names = {:new => 'nuevo', 'rss' => 'canal', :fix => 'corrigir' } + + with_restful_routing :messages, + :collection => collection_methods, + :member => member_methods, + :path_names => path_names do + + assert_restful_routes_for :messages, + :collection => collection_methods, + :member => member_methods, + :path_names => path_names do |options| + member_methods.each do |action, method| + assert_recognizes(options.merge(:action => action.to_s, :id => '1'), + :path => "/messages/1/#{path_names[action] || action}", + :method => method) + end + + collection_methods.each do |action, method| + assert_recognizes(options.merge(:action => action), + :path => "/messages/#{path_names[action] || action}", + :method => method) + end + end + + assert_restful_named_routes_for :messages, + :collection => collection_methods, + :member => member_methods, + :path_names => path_names do |options| + + collection_methods.keys.each do |action| + assert_named_route "/messages/#{path_names[action] || action}", "#{action}_messages_path", :action => action + end + + member_methods.keys.each do |action| + assert_named_route "/messages/1/#{path_names[action] || action}", "#{action}_message_path", :action => action, :id => "1" + end + + end + end + end + def test_override_paths_for_default_restful_actions resource = ActionController::Resources::Resource.new(:messages, :path_names => {:new => 'nuevo', :edit => 'editar'}) -- 1.6.4.2 From 7908cfabf7b545707a21e0bd795eb45cf4ea45d6 Mon Sep 17 00:00:00 2001 From: Tristan Dunn Date: Sun, 9 Aug 2009 16:22:53 -0400 Subject: [PATCH 210/779] No longer require database name for MySQL to allow cross database selects. [#1122 state:committed] Signed-off-by: Jeremy Kemper --- .../connection_adapters/mysql_adapter.rb | 7 +------ activerecord/test/cases/adapter_test.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 4542fe6..3c5e4f5 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -52,12 +52,7 @@ module ActiveRecord socket = config[:socket] username = config[:username] ? config[:username].to_s : 'root' password = config[:password].to_s - - if config.has_key?(:database) - database = config[:database] - else - raise ArgumentError, "No database specified. Missing argument: database." - end + database = config[:database] # Require the MySQL driver and define Mysql::Result.all_hashes unless defined? Mysql diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 0477064..3dd3dd8 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -63,6 +63,18 @@ class AdapterTest < ActiveRecord::TestCase def test_show_nonexistent_variable_returns_nil assert_nil @connection.show_variable('foo_bar_baz') end + + def test_not_specifying_database_name_for_cross_database_selects + assert_nothing_raised do + ActiveRecord::Base.establish_connection({ + :adapter => 'mysql', + :username => 'rails' + }) + ActiveRecord::Base.connection.execute "SELECT activerecord_unittest.pirates.*, activerecord_unittest2.courses.* FROM activerecord_unittest.pirates, activerecord_unittest2.courses" + end + + ActiveRecord::Base.establish_connection 'arunit' + end end if current_adapter?(:PostgreSQLAdapter) -- 1.6.4.2 From 93f5d9d5f0417c552701996760cc5de2d2d4be10 Mon Sep 17 00:00:00 2001 From: Vladimir Meremyanin Date: Sun, 9 Aug 2009 23:09:08 +0100 Subject: [PATCH 211/779] Make sure association conditions work with :include and :joins [#358 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/base.rb | 8 +++++--- .../associations/has_many_associations_test.rb | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 4bb48ee..54ff9e5 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2303,11 +2303,13 @@ module ActiveRecord #:nodoc: # Extract table name from qualified attribute names. if attr.include?('.') - table_name, attr = attr.split('.', 2) - table_name = connection.quote_table_name(table_name) + attr_table_name, attr = attr.split('.', 2) + attr_table_name = connection.quote_table_name(attr_table_name) + else + attr_table_name = table_name end - attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value) + attribute_condition("#{attr_table_name}.#{connection.quote_column_name(attr)}", value) else sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s)) end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index c78dba1..ee2e2e3 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -279,6 +279,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal client2, firm.clients.find(:first, :conditions => ["#{QUOTED_TYPE} = :type", { :type => 'Client' }]) end + def test_find_all_with_include_and_conditions + assert_nothing_raised do + Developer.find(:all, :joins => :audit_logs, :conditions => {'audit_logs.message' => nil, :name => 'Smith'}) + end + end + def test_find_in_collection assert_equal Client.find(2).name, companies(:first_firm).clients.find(2).name assert_raise(ActiveRecord::RecordNotFound) { companies(:first_firm).clients.find(6) } -- 1.6.4.2 From be017fd7d54c9203dc574d8e1a6f22d80df0e899 Mon Sep 17 00:00:00 2001 From: Jordan Brough Date: Sun, 9 Aug 2009 23:31:16 +0100 Subject: [PATCH 212/779] Active Resource recognizes 410 as Resource Gone now [#2316 state:resolved] [Jordan Brough, Jatinder Singh] Signed-off-by: Pratik Naik Conflicts: activeresource/lib/active_resource/exceptions.rb Signed-off-by: Pratik Naik --- activeresource/CHANGELOG | 2 + activeresource/lib/active_resource/base.rb | 3 +- activeresource/lib/active_resource/connection.rb | 5 ++ activeresource/lib/active_resource/exceptions.rb | 66 ++++++++++++++++++++++ activeresource/test/base_test.rb | 24 ++++++++ activeresource/test/connection_test.rb | 3 + 6 files changed, 102 insertions(+), 1 deletions(-) create mode 100644 activeresource/lib/active_resource/exceptions.rb diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index caabb7d..fcd8634 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,5 +1,7 @@ *2.3.4 (pending)* +* Recognizes 410 as Resource Gone. #2316 [Jordan Brough, Jatinder Singh] + * More thorough SSL support. #2370 [Roy Nicholson] * HTTP proxy support. #2133 [Marshall Huss, Sébastien Dabet] diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index d9d1016..73253c4 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -153,6 +153,7 @@ module ActiveResource # * 404 - ActiveResource::ResourceNotFound # * 405 - ActiveResource::MethodNotAllowed # * 409 - ActiveResource::ResourceConflict + # * 410 - ActiveResource::ResourceGone # * 422 - ActiveResource::ResourceInvalid (rescued by save as validation errors) # * 401..499 - ActiveResource::ClientError # * 500..599 - ActiveResource::ServerError @@ -615,7 +616,7 @@ module ActiveResource response.code.to_i == 200 end # id && !find_single(id, options).nil? - rescue ActiveResource::ResourceNotFound + rescue ActiveResource::ResourceNotFound, ActiveResource::ResourceGone false end diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb index acb9ff5..768b8c9 100644 --- a/activeresource/lib/active_resource/connection.rb +++ b/activeresource/lib/active_resource/connection.rb @@ -57,6 +57,9 @@ module ActiveResource # 409 Conflict class ResourceConflict < ClientError; end # :nodoc: + # 410 Gone + class ResourceGone < ClientError; end # :nodoc: + # 5xx Server Error class ServerError < ConnectionError; end # :nodoc: @@ -193,6 +196,8 @@ module ActiveResource raise(MethodNotAllowed.new(response)) when 409 raise(ResourceConflict.new(response)) + when 410 + raise(ResourceGone.new(response)) when 422 raise(ResourceInvalid.new(response)) when 401...500 diff --git a/activeresource/lib/active_resource/exceptions.rb b/activeresource/lib/active_resource/exceptions.rb new file mode 100644 index 0000000..0631cdc --- /dev/null +++ b/activeresource/lib/active_resource/exceptions.rb @@ -0,0 +1,66 @@ +module ActiveResource + class ConnectionError < StandardError # :nodoc: + attr_reader :response + + def initialize(response, message = nil) + @response = response + @message = message + end + + def to_s + "Failed with #{response.code} #{response.message if response.respond_to?(:message)}" + end + end + + # Raised when a Timeout::Error occurs. + class TimeoutError < ConnectionError + def initialize(message) + @message = message + end + def to_s; @message ;end + end + + # Raised when a OpenSSL::SSL::SSLError occurs. + class SSLError < ConnectionError + def initialize(message) + @message = message + end + def to_s; @message ;end + end + + # 3xx Redirection + class Redirection < ConnectionError # :nodoc: + def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end + end + + # 4xx Client Error + class ClientError < ConnectionError; end # :nodoc: + + # 400 Bad Request + class BadRequest < ClientError; end # :nodoc + + # 401 Unauthorized + class UnauthorizedAccess < ClientError; end # :nodoc + + # 403 Forbidden + class ForbiddenAccess < ClientError; end # :nodoc + + # 404 Not Found + class ResourceNotFound < ClientError; end # :nodoc: + + # 409 Conflict + class ResourceConflict < ClientError; end # :nodoc: + + # 410 Gone + class ResourceGone < ClientError; end # :nodoc: + + # 5xx Server Error + class ServerError < ConnectionError; end # :nodoc: + + # 405 Method Not Allowed + class MethodNotAllowed < ClientError # :nodoc: + def allowed_methods + @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym } + end + end +end diff --git a/activeresource/test/base_test.rb b/activeresource/test/base_test.rb index e725d8e..527e099 100644 --- a/activeresource/test/base_test.rb +++ b/activeresource/test/base_test.rb @@ -898,6 +898,14 @@ class BaseTest < Test::Unit::TestCase assert_raise(ActiveResource::ResourceNotFound) { StreetAddress.find(1, :params => { :person_id => 1 }) } end + def test_destroy_with_410_gone + assert Person.find(1).destroy + ActiveResource::HttpMock.respond_to do |mock| + mock.get "/people/1.xml", {}, nil, 410 + end + assert_raise(ActiveResource::ResourceGone) { Person.find(1).destroy } + end + def test_delete assert Person.delete(1) ActiveResource::HttpMock.respond_to do |mock| @@ -913,6 +921,14 @@ class BaseTest < Test::Unit::TestCase end assert_raise(ActiveResource::ResourceNotFound) { StreetAddress.find(1, :params => { :person_id => 1 }) } end + + def test_delete_with_410_gone + assert Person.delete(1) + ActiveResource::HttpMock.respond_to do |mock| + mock.get "/people/1.xml", {}, nil, 410 + end + assert_raise(ActiveResource::ResourceGone) { Person.find(1) } + end def test_exists # Class method. @@ -973,6 +989,14 @@ class BaseTest < Test::Unit::TestCase assert Person.exists?('not-mocked') end + def test_exists_with_410_gone + ActiveResource::HttpMock.respond_to do |mock| + mock.head "/people/1.xml", {}, nil, 410 + end + + assert !Person.exists?(1) + end + def test_to_xml matz = Person.find(1) xml = matz.encode diff --git a/activeresource/test/connection_test.rb b/activeresource/test/connection_test.rb index b482f2d..d7466c6 100644 --- a/activeresource/test/connection_test.rb +++ b/activeresource/test/connection_test.rb @@ -56,6 +56,9 @@ class ConnectionTest < Test::Unit::TestCase # 409 is an optimistic locking error assert_response_raises ActiveResource::ResourceConflict, 409 + # 410 is a removed resource + assert_response_raises ActiveResource::ResourceGone, 410 + # 422 is a validation error assert_response_raises ActiveResource::ResourceInvalid, 422 -- 1.6.4.2 From 59c3b0d0deb8ddd83f20c8a66fde188aed74c51c Mon Sep 17 00:00:00 2001 From: Grzegorz Forysinski Date: Sun, 9 Aug 2009 18:24:49 -0400 Subject: [PATCH 213/779] Ensure ActiveResource#load works with numeric arrays [Grzegorz Forysinski, Elad Meidar] [#2305 state:resolved] Signed-off-by: Pratik Naik --- activeresource/lib/active_resource/base.rb | 8 +++++++- activeresource/test/base/load_test.rb | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 73253c4..b176e61 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -1016,7 +1016,13 @@ module ActiveResource case value when Array resource = find_or_create_resource_for_collection(key) - value.map { |attrs| attrs.is_a?(String) ? attrs.dup : resource.new(attrs) } + value.map do |attrs| + if attrs.is_a?(String) || attrs.is_a?(Numeric) + attrs.duplicable? ? attrs.dup : attrs + else + resource.new(attrs) + end + end when Hash resource = find_or_create_resource_for(key) resource.new(value) diff --git a/activeresource/test/base/load_test.rb b/activeresource/test/base/load_test.rb index a475fab..e4121d3 100644 --- a/activeresource/test/base/load_test.rb +++ b/activeresource/test/base/load_test.rb @@ -49,7 +49,9 @@ class BaseLoadTest < Test::Unit::TestCase :id => 1, :state => { :id => 1, :name => 'Oregon', :notable_rivers => [ { :id => 1, :name => 'Willamette' }, - { :id => 2, :name => 'Columbia', :rafted_by => @matz }] }}} + { :id => 2, :name => 'Columbia', :rafted_by => @matz }], + :postal_codes => [97018,1234567890], + :places => ["Columbia City", "Unknown"]}}} @person = Person.new end @@ -125,6 +127,19 @@ class BaseLoadTest < Test::Unit::TestCase assert_kind_of Person::Street::State::NotableRiver, rivers.first assert_equal @deep[:street][:state][:notable_rivers].first[:id], rivers.first.id assert_equal @matz[:id], rivers.last.rafted_by.id + + postal_codes = state.postal_codes + assert_kind_of Array, postal_codes + assert_equal 2, postal_codes.size + assert_kind_of Fixnum, postal_codes.first + assert_equal @deep[:street][:state][:postal_codes].first, postal_codes.first + assert_kind_of Bignum, postal_codes.last + assert_equal @deep[:street][:state][:postal_codes].last, postal_codes.last + + places = state.places + assert_kind_of Array, places + assert_kind_of String, places.first + assert_equal @deep[:street][:state][:places].first, places.first end def test_nested_collections_within_the_same_namespace -- 1.6.4.2 From ba961250bd07a131329fb90cc998c1f28d5aac14 Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Sat, 8 Aug 2009 11:50:17 -0400 Subject: [PATCH 214/779] Fixed to_label_tag to accept id attribute without changing for attribute [#2660 status:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_view/helpers/form_helper.rb | 1 + actionpack/test/template/form_helper_test.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 2ac407c..8efa9a0 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -726,6 +726,7 @@ module ActionView options = options.stringify_keys tag_value = options.delete("value") name_and_id = options.dup + name_and_id["id"] = name_and_id["for"] add_default_name_and_id_for_value(tag_value, name_and_id) options.delete("index") options["for"] ||= name_and_id["id"] diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index e92f62d..5756456 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -145,6 +145,22 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal('', label(:post, :title, nil, "for" => "my_for")) end + def test_label_with_id_attribute_as_symbol + assert_dom_equal('', label(:post, :title, nil, :id => "my_id")) + end + + def test_label_with_id_attribute_as_string + assert_dom_equal('', label(:post, :title, nil, "id" => "my_id")) + end + + def test_label_with_for_and_id_attributes_as_symbol + assert_dom_equal('', label(:post, :title, nil, :for => "my_for", :id => "my_id")) + end + + def test_label_with_for_and_id_attributes_as_string + assert_dom_equal('', label(:post, :title, nil, "for" => "my_for", "id" => "my_id")) + end + def test_label_for_radio_buttons_with_value assert_dom_equal('', label("post", "title", "The title goes here", :value => "great_title")) assert_dom_equal('', label("post", "title", "The title goes here", :value => "great title")) -- 1.6.4.2 From 18b4ac6992bdd51b9c279c064b60692bb80235ba Mon Sep 17 00:00:00 2001 From: Arthur Zapparoli Date: Sun, 9 Aug 2009 14:42:38 -0300 Subject: [PATCH 215/779] Model#human_attribute_name now accept symbols [#3025 status:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 2 +- activerecord/test/cases/i18n_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 54ff9e5..53d3d20 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1364,7 +1364,7 @@ module ActiveRecord #:nodoc: end defaults << options[:default] if options[:default] defaults.flatten! - defaults << attribute_key_name.humanize + defaults << attribute_key_name.to_s.humanize options[:count] ||= 1 I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:activerecord, :attributes])) end diff --git a/activerecord/test/cases/i18n_test.rb b/activerecord/test/cases/i18n_test.rb index b1db662..d59c53c 100644 --- a/activerecord/test/cases/i18n_test.rb +++ b/activerecord/test/cases/i18n_test.rb @@ -12,6 +12,11 @@ class ActiveRecordI18nTests < Test::Unit::TestCase I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } } assert_equal 'topic title attribute', Topic.human_attribute_name('title') end + + def test_translated_model_attributes_with_symbols + I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } } + assert_equal 'topic title attribute', Topic.human_attribute_name(:title) + end def test_translated_model_attributes_with_sti I18n.backend.store_translations 'en', :activerecord => {:attributes => {:reply => {:title => 'reply title attribute'} } } -- 1.6.4.2 From 9bcacf49627ebe8467014b93de34fa8fa5ac9cd4 Mon Sep 17 00:00:00 2001 From: Arthur Zapparoli Date: Sun, 9 Aug 2009 16:19:23 -0300 Subject: [PATCH 216/779] Removed duplicated tests [#3026 state:resolved] Signed-off-by: Pratik Naik --- .../associations/belongs_to_associations_test.rb | 18 ------------------ 1 files changed, 0 insertions(+), 18 deletions(-) diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 970601c..9f3945f 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -249,24 +249,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal 1, Topic.find(topic.id)[:replies_count] end - def test_belongs_to_counter_after_save - topic = Topic.create("title" => "monday night") - topic.replies.create("title" => "re: monday night", "content" => "football") - assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count") - - topic.save - assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count") - end - - def test_belongs_to_counter_after_update_attributes - topic = Topic.create("title" => "37s") - topic.replies.create("title" => "re: 37s", "content" => "rails") - assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count") - - topic.update_attributes("title" => "37signals") - assert_equal 1, Topic.find(topic.id).send(:read_attribute, "replies_count") - end - def test_assignment_before_child_saved final_cut = Client.new("name" => "Final Cut") firm = Firm.find(1) -- 1.6.4.2 From b763858ed5faeda720035dd2178e7c44aa34240a Mon Sep 17 00:00:00 2001 From: Gabe da Silveira Date: Sat, 8 Aug 2009 20:58:32 -0700 Subject: [PATCH 217/779] Enable has_many :through for going through a has_one association on the join model [#2719 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 32 ++++++++++++++++++- .../associations/has_many_through_association.rb | 2 +- activerecord/lib/active_record/reflection.rb | 2 +- .../associations/has_many_associations_test.rb | 2 +- .../has_many_through_associations_test.rb | 12 +++++++ .../test/cases/associations/join_model_test.rb | 2 +- activerecord/test/models/author.rb | 1 + 7 files changed, 47 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 7732bb5..ba603b0 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -34,11 +34,13 @@ module ActiveRecord end end - class HasManyThroughCantAssociateThroughHasManyReflection < ActiveRecordError #:nodoc: + class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError #:nodoc: def initialize(owner, reflection) super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.") end end + HasManyThroughCantAssociateThroughHasManyReflection = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection', 'ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection') + class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc: def initialize(owner, reflection) super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.") @@ -410,6 +412,32 @@ module ActiveRecord # @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm # @firm.invoices # selects all invoices by going through the Client join model. # + # Similarly you can go through a +has_one+ association on the join model: + # + # class Group < ActiveRecord::Base + # has_many :users + # has_many :avatars, :through => :users + # end + # + # class User < ActiveRecord::Base + # belongs_to :group + # has_one :avatar + # end + # + # class Avatar < ActiveRecord::Base + # belongs_to :user + # end + # + # @group = Group.first + # @group.users.collect { |u| u.avatar }.flatten # select all avatars for all users in the group + # @group.avatars # selects all avatars by going through the User join model. + # + # An important caveat with going through +has_one+ or +has_many+ associations on the join model is that these associations are + # *read-only*. For example, the following would not work following the previous example: + # + # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around. + # @group.avatars.delete(@group.avatars.last) # so would this + # # === Polymorphic Associations # # Polymorphic associations on models are not restricted on what types of models they can be associated with. Rather, they @@ -759,7 +787,7 @@ module ActiveRecord # [:through] # Specifies a Join Model through which to perform the query. Options for :class_name and :foreign_key # are ignored, as the association uses the source reflection. You can only use a :through query through a belongs_to - # or has_many association on the join model. + # has_one or has_many association on the join model. # [:source] # Specifies the source association name used by has_many :through queries. Only use it if the name cannot be # inferred from the association. has_many :subscribers, :through => :subscriptions will look for either :subscribers or diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index ac789e2..df2ef64 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -96,7 +96,7 @@ module ActiveRecord # Construct attributes for :through pointing to owner and associate. def construct_join_attributes(associate) # TODO: revist this to allow it for deletion, supposing dependent option is supported - raise ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection.new(@owner, @reflection) if @reflection.source_reflection.macro == :has_many + raise ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(@owner, @reflection) if [:has_one, :has_many].include?(@reflection.source_reflection.macro) join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id) if @reflection.options[:source_type] join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 2d4c1d5..54b8c61 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -297,7 +297,7 @@ module ActiveRecord raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection) end - unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil? + unless [:belongs_to, :has_many, :has_one].include?(source_reflection.macro) && source_reflection.options[:through].nil? raise HasManyThroughSourceAssociationMacroError.new(self) end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index ee2e2e3..9a5758f 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -889,7 +889,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase lambda { authors(:mary).comments = [comments(:greetings), comments(:more_greetings)] }, lambda { authors(:mary).comments << Comment.create!(:body => "Yay", :post_id => 424242) }, lambda { authors(:mary).comments.delete(authors(:mary).comments.first) }, - ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection, &block) } + ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) } end def test_dynamic_find_should_respect_association_order_for_through 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 a8cdfc5..7453eb0 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -281,4 +281,16 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase post_with_no_comments = people(:michael).posts_with_no_comments.first assert_equal post_with_no_comments, posts(:authorless) end + + def test_has_many_through_has_one_reflection + assert_equal [comments(:eager_sti_on_associations_vs_comment)], authors(:david).very_special_comments + end + + def test_modifying_has_many_through_has_one_reflection_should_raise + [ + lambda { authors(:david).very_special_comments = [VerySpecialComment.create!(:body => "Gorp!", :post_id => 1011), VerySpecialComment.create!(:body => "Eep!", :post_id => 1012)] }, + lambda { authors(:david).very_special_comments << VerySpecialComment.create!(:body => "Hoohah!", :post_id => 1013) }, + lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) }, + ].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) } + end end diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index b1060d0..d478180 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -377,7 +377,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_has_many_through_polymorphic_has_one - assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tagging } + assert_equal Tagging.find(1,2), authors(:david).tagging end def test_has_many_through_polymorphic_has_many diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index b844c7c..f264f98 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -1,5 +1,6 @@ class Author < ActiveRecord::Base has_many :posts + has_many :very_special_comments, :through => :posts has_many :posts_with_comments, :include => :comments, :class_name => "Post" has_many :popular_grouped_posts, :include => :comments, :class_name => "Post", :group => "type", :having => "SUM(comments_count) > 1", :select => "type" has_many :posts_with_comments_sorted_by_comment_id, :include => :comments, :class_name => "Post", :order => 'comments.id' -- 1.6.4.2 From 491f1b5f36a11427decc5d7f3558b5c3f5f243c1 Mon Sep 17 00:00:00 2001 From: Tristan Dunn Date: Mon, 10 Aug 2009 00:41:36 +0100 Subject: [PATCH 218/779] Prevent overwriting of table name in merging SQL conditions [#2949 state:resolved] --- activerecord/lib/active_record/base.rb | 4 +++- activerecord/test/cases/named_scope_test.rb | 6 ++++++ activerecord/test/models/comment.rb | 6 +++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 53d3d20..c17702d 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2294,10 +2294,12 @@ module ActiveRecord #:nodoc: # And for value objects on a composed_of relationship: # { :address => Address.new("123 abc st.", "chicago") } # # => "address_street='123 abc st.' and address_city='chicago'" - def sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name) + def sanitize_sql_hash_for_conditions(attrs, default_table_name = quoted_table_name) attrs = expand_hash_conditions_for_aggregates(attrs) conditions = attrs.map do |attr, value| + table_name = default_table_name + unless value.is_a?(Hash) attr = attr.to_s diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 0d63c78..208a2ee 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -332,6 +332,12 @@ class NamedScopeTest < ActiveRecord::TestCase end end end + + def test_table_names_for_chaining_scopes_with_and_without_table_name_included + assert_nothing_raised do + Comment.for_first_post.for_first_author.all + end + end end class DynamicScopeMatchTest < ActiveRecord::TestCase diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index f7f07c1..399dea9 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -1,6 +1,10 @@ class Comment < ActiveRecord::Base named_scope :containing_the_letter_e, :conditions => "comments.body LIKE '%e%'" - + named_scope :for_first_post, :conditions => { :post_id => 1 } + named_scope :for_first_author, + :joins => :post, + :conditions => { "posts.author_id" => 1 } + belongs_to :post, :counter_cache => true def self.what_are_you -- 1.6.4.2 From ecc9b705d7d12f853c794ca564b8e72995563eae Mon Sep 17 00:00:00 2001 From: Visnu Pitiyanuvath Date: Thu, 11 Jun 2009 16:49:49 -0700 Subject: [PATCH 219/779] Allow ho:through#build when the owner is a new record [#1749 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 6 +++++- .../associations/has_one_through_association.rb | 10 ++++++++-- .../has_one_through_associations_test.rb | 10 ++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index ba603b0..a08119c 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1269,7 +1269,11 @@ module ActiveRecord if association_proxy_class == HasOneThroughAssociation association.create_through_record(new_value) - self.send(reflection.name, new_value) + if new_record? + association_instance_set(reflection.name, new_value.nil? ? nil : association) + else + self.send(reflection.name, new_value) + end else association.replace(new_value) association_instance_set(reflection.name, new_value.nil? ? nil : association) 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 d93c8e7..d8bcf76 100644 --- a/activerecord/lib/active_record/associations/has_one_through_association.rb +++ b/activerecord/lib/active_record/associations/has_one_through_association.rb @@ -9,8 +9,14 @@ module ActiveRecord if current_object 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))) if new_value + elsif new_value + if @owner.new_record? + self.target = new_value + through_association = @owner.send(:association_instance_get, @reflection.through_reflection.name) + through_association.build(construct_join_attributes(new_value)) + else + @owner.send(@reflection.through_reflection.name, klass.create(construct_join_attributes(new_value))) + end end 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 ab6e6d2..9aef3eb 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -28,6 +28,16 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase assert_not_nil new_member.current_membership assert_not_nil new_member.club end + + def test_creating_association_builds_through_record_for_new + new_member = Member.new(:name => "Jane") + new_member.club = clubs(:moustache_club) + assert new_member.current_membership + assert_equal clubs(:moustache_club), new_member.current_membership.club + assert_equal clubs(:moustache_club), new_member.club + assert new_member.save + assert_equal clubs(:moustache_club), new_member.club + end def test_replace_target_record new_club = Club.create(:name => "Marx Bros") -- 1.6.4.2 From 9e29c084eb0551eb18b666c8df53b9985af8aa16 Mon Sep 17 00:00:00 2001 From: Max Lapshin Date: Mon, 16 Mar 2009 18:20:15 +0300 Subject: [PATCH 220/779] Make sure link_to generates the form with the specified :href if any [#2254 state:resolved] Signed-off-by: Pratik Naik --- actionpack/lib/action_view/helpers/url_helper.rb | 2 +- actionpack/test/template/url_helper_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 9270886..f692af1 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -568,7 +568,7 @@ module ActionView when confirm && popup "if (#{confirm_javascript_function(confirm)}) { #{popup_javascript_function(popup)} };return false;" when confirm && method - "if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method)} };return false;" + "if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method, url, href)} };return false;" when confirm "return #{confirm_javascript_function(confirm)};" when method diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 5900709..82292cb 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -219,6 +219,14 @@ class UrlHelperTest < ActionView::TestCase ) end + def test_link_tag_using_delete_javascript_and_href_and_confirm + assert_dom_equal( + "Destroy", + link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :confirm => "Are you serious?"), + "When specifying url, form should be generated with it, but not this.href" + ) + end + def test_link_tag_using_post_javascript_and_popup assert_raise(ActionView::ActionViewError) { link_to("Hello", "http://www.example.com", :popup => true, :method => :post, :confirm => "Are you serious?") } end -- 1.6.4.2 From cc3183d4bec6855b9b5931759a22dd228dfe7687 Mon Sep 17 00:00:00 2001 From: jeem Date: Sun, 9 Aug 2009 20:04:15 -0500 Subject: [PATCH 221/779] make private_and_public_methods unmemoizable [#2372 state:resolved] Signed-off-by: Joshua Peek --- activesupport/lib/active_support/memoizable.rb | 2 +- .../flush_cache_on_private_memoization_test.rb | 44 ++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletions(-) create mode 100644 activesupport/test/flush_cache_on_private_memoization_test.rb diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 71cfe61..9282bab 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -43,7 +43,7 @@ module ActiveSupport def flush_cache(*syms, &block) syms.each do |sym| - methods.each do |m| + (methods + private_methods + protected_methods).each do |m| if m.to_s =~ /^_unmemoized_(#{sym})/ ivar = ActiveSupport::Memoizable.memoized_ivar_for($1) instance_variable_get(ivar).clear if instance_variable_defined?(ivar) diff --git a/activesupport/test/flush_cache_on_private_memoization_test.rb b/activesupport/test/flush_cache_on_private_memoization_test.rb new file mode 100644 index 0000000..ddbd05b --- /dev/null +++ b/activesupport/test/flush_cache_on_private_memoization_test.rb @@ -0,0 +1,44 @@ +require 'rubygems' +require 'activesupport' +require 'test/unit' + +class FlashCacheOnPrivateMemoizationTest < Test::Unit::TestCase + extend ActiveSupport::Memoizable + + def test_public + assert_method_unmemoizable :pub + end + + def test_protected + assert_method_unmemoizable :prot + end + + def test_private + assert_method_unmemoizable :priv + end + + def pub; rand end + memoize :pub + + protected + + def prot; rand end + memoize :prot + + private + + def priv; rand end + memoize :priv + + def assert_method_unmemoizable(meth, message=nil) + full_message = build_message(message, " not unmemoizable.\n", meth) + assert_block(full_message) do + a = send meth + b = send meth + unmemoize_all + c = send meth + a == b && a != c + end + end + +end \ No newline at end of file -- 1.6.4.2 From 33c054d7e04672201e120a0f8da38166650f3a94 Mon Sep 17 00:00:00 2001 From: Dan Croak Date: Sun, 9 Aug 2009 21:32:00 -0400 Subject: [PATCH 222/779] has_many :through create should not raise validation errors [#2934 state:committed] Signed-off-by: Jeremy Kemper --- .../associations/has_many_through_association.rb | 12 +++++++- .../has_many_through_associations_test.rb | 30 +++++++++++++++++++- activerecord/test/cases/reflection_test.rb | 4 +- activerecord/test/models/company.rb | 2 + activerecord/test/models/contract.rb | 5 +++ activerecord/test/schema/schema.rb | 4 ++ 6 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 activerecord/test/models/contract.rb 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 df2ef64..6af14a5 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -17,7 +17,17 @@ module ActiveRecord def create(attrs = nil) transaction do - self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association } : @reflection.create_association) + object = if attrs + @reflection.klass.send(:with_scope, :create => attrs) { + @reflection.create_association + } + else + @reflection.create_association + end + raise_on_type_mismatch(object) + add_record_to_target_with_callbacks(object) do |r| + insert_record(object, false) + end object end end 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 7453eb0..a43f876 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -11,9 +11,13 @@ require 'models/author' require 'models/owner' require 'models/pet' require 'models/toy' +require 'models/contract' +require 'models/company' +require 'models/developer' class HasManyThroughAssociationsTest < ActiveRecord::TestCase - fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys + fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys, + :companies def test_associate_existing assert_queries(2) { posts(:thinking);people(:david) } @@ -157,6 +161,30 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_equal peeps + 1, posts(:thinking).people.count end + def test_associate_with_create_and_invalid_options + peeps = companies(:first_firm).developers.count + assert_nothing_raised { companies(:first_firm).developers.create(:name => '0') } + assert_equal peeps, companies(:first_firm).developers.count + end + + def test_associate_with_create_and_valid_options + peeps = companies(:first_firm).developers.count + assert_nothing_raised { companies(:first_firm).developers.create(:name => 'developer') } + assert_equal peeps + 1, companies(:first_firm).developers.count + end + + def test_associate_with_create_bang_and_invalid_options + peeps = companies(:first_firm).developers.count + assert_raises(ActiveRecord::RecordInvalid) { companies(:first_firm).developers.create!(:name => '0') } + assert_equal peeps, companies(:first_firm).developers.count + end + + def test_associate_with_create_bang_and_valid_options + peeps = companies(:first_firm).developers.count + assert_nothing_raised { companies(:first_firm).developers.create!(:name => 'developer') } + assert_equal peeps + 1, companies(:first_firm).developers.count + end + def test_clear_associations assert_queries(2) { posts(:welcome);posts(:welcome).people(true) } diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 30ec157..a0107d2 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -170,8 +170,8 @@ class ReflectionTest < ActiveRecord::TestCase def test_reflection_of_all_associations # FIXME these assertions bust a lot - assert_equal 28, Firm.reflect_on_all_associations.size - assert_equal 21, Firm.reflect_on_all_associations(:has_many).size + assert_equal 30, Firm.reflect_on_all_associations.size + assert_equal 23, Firm.reflect_on_all_associations(:has_many).size assert_equal 7, Firm.reflect_on_all_associations(:has_one).size assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 2a65b03..d4bbbeb 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -9,6 +9,8 @@ class Company < AbstractCompany validates_presence_of :name has_one :dummy_account, :foreign_key => "firm_id", :class_name => "Account" + has_many :contracts + has_many :developers, :through => :contracts def arbitrary_method "I am Jack's profound disappointment" diff --git a/activerecord/test/models/contract.rb b/activerecord/test/models/contract.rb new file mode 100644 index 0000000..5235bfa --- /dev/null +++ b/activerecord/test/models/contract.rb @@ -0,0 +1,5 @@ +class Contract < ActiveRecord::Base + belongs_to :company + belongs_to :developer +end + diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index d080140..87fad0e 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -125,6 +125,10 @@ ActiveRecord::Schema.define do t.integer :extendedWarranty, :null => false end + create_table :contracts, :force => true do |t| + t.integer :developer_id + t.integer :company_id + end create_table :customers, :force => true do |t| t.string :name -- 1.6.4.2 From 15fd67e9d8155181695add77c8a7b11d92564391 Mon Sep 17 00:00:00 2001 From: Joshua Nichols Date: Sun, 9 Aug 2009 22:16:30 -0400 Subject: [PATCH 223/779] Backported XML serialization behavior from master for dealing with root nodes that have modules. ie, - the root node is dasherized, such that MyApplication::Business::Project becomes - association children nodes have type attributes, such that MyApplication::Business::Developer becomes [#2723 state:committed] Signed-off-by: Jeremy Kemper --- .../active_record/serializers/xml_serializer.rb | 8 ++++++-- activerecord/test/cases/xml_serialization_test.rb | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index fa75874..9bf1193 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -178,7 +178,7 @@ module ActiveRecord #:nodoc: end def root - root = (options[:root] || @record.class.to_s.underscore).to_s + root = (options[:root] || @record.class.model_name.singular).to_s reformat_name(root) end @@ -320,7 +320,11 @@ module ActiveRecord #:nodoc: protected def compute_type - type = @record.class.serialized_attributes.has_key?(name) ? :yaml : @record.class.columns_hash[name].type + type = if @record.class.serialized_attributes.has_key?(name) + :yaml + else + @record.class.columns_hash[name].try(:type) + end case type when :text diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb index b499976..d8d952b 100644 --- a/activerecord/test/cases/xml_serialization_test.rb +++ b/activerecord/test/cases/xml_serialization_test.rb @@ -4,6 +4,7 @@ require 'models/post' require 'models/author' require 'models/tagging' require 'models/comment' +require 'models/company_in_module' class XmlSerializationTest < ActiveRecord::TestCase def test_should_serialize_default_root @@ -129,6 +130,25 @@ class NilXmlSerializationTest < ActiveRecord::TestCase end end +class DatabaseConnectedXmlModuleSerializationTest < ActiveRecord::TestCase + + fixtures :projects, :developers, :developers_projects + + def test_module + project = MyApplication::Business::Project.find :first + xml = project.to_xml + assert_match %r{}, xml + assert_match %r{}, xml + end + + def test_module_with_include + project = MyApplication::Business::Project.find :first + xml = project.to_xml :include => :developers + assert_match %r{}, xml + assert_match %r{}, xml + end +end + class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase fixtures :authors, :posts # to_xml used to mess with the hash the user provided which -- 1.6.4.2 From ebb6606a4d09ca362a0c56a458444a8dd551c495 Mon Sep 17 00:00:00 2001 From: Joshua Nichols Date: Sat, 8 Aug 2009 17:19:47 -0400 Subject: [PATCH 224/779] Only load db/schema.rb if it exists; otherwise, display a message to run db:migrate or remove active_record in environment.rb. [#3012 state:committed] Signed-off-by: Jeremy Kemper --- railties/lib/tasks/databases.rake | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-) diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake index 9588fab..fbad8a1 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/tasks/databases.rake @@ -256,7 +256,11 @@ namespace :db do desc "Load a schema.rb file into the database" task :load => :environment do file = ENV['SCHEMA'] || "#{RAILS_ROOT}/db/schema.rb" - load(file) + if File.exists?(file) + load(file) + else + abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{RAILS_ROOT}/config/environment.rb to prevent active_record from loading: config.frameworks -= [ :active_record ]} + end end end -- 1.6.4.2 From ff643ce9679ad66a5cd79c90c096496d07d6efaf Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 9 Aug 2009 22:38:50 -0500 Subject: [PATCH 225/779] Deprecate router generation "best match" sorting --- actionpack/lib/action_controller/resources.rb | 4 +- .../lib/action_controller/routing/route_set.rb | 32 +++++++++++++++++-- actionpack/test/controller/caching_test.rb | 2 +- actionpack/test/controller/routing_test.rb | 8 +++-- actionpack/test/controller/url_rewriter_test.rb | 6 ++-- 5 files changed, 39 insertions(+), 13 deletions(-) diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index 2d3f82a..919fb75 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -526,13 +526,13 @@ module ActionController resource = Resource.new(entities, options) with_options :controller => resource.controller do |map| + map_associations(resource, options) + map_collection_actions(map, resource) map_default_collection_actions(map, resource) map_new_actions(map, resource) map_member_actions(map, resource) - map_associations(resource, options) - if block_given? with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block) end diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 8fdf64c..41236c1 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -407,9 +407,24 @@ module ActionController # don't use the recalled keys when determining which routes to check routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }] - routes.each do |route| + routes[1].each_with_index do |route, index| results = route.__send__(method, options, merged, expire_on) - return results if results && (!results.is_a?(Array) || results.first) + if results && (!results.is_a?(Array) || results.first) + + # Compare results with Rails 3.0 behavior + if routes[0][index] != route + routes[0].each do |route2| + new_results = route2.__send__(method, options, merged, expire_on) + if new_results && (!new_results.is_a?(Array) || new_results.first) + ActiveSupport::Deprecation.warn "The URL you generated will use the first matching route in routes.rb rather than the \"best\" match. " + + "In Rails 3.0 #{new_results} would of been generated instead of #{results}" + break + end + end + end + + return results + end end end @@ -448,7 +463,10 @@ module ActionController @routes_by_controller ||= Hash.new do |controller_hash, controller| controller_hash[controller] = Hash.new do |action_hash, action| action_hash[action] = Hash.new do |key_hash, keys| - key_hash[keys] = routes_for_controller_and_action_and_keys(controller, action, keys) + key_hash[keys] = [ + routes_for_controller_and_action_and_keys(controller, action, keys), + deprecated_routes_for_controller_and_action_and_keys(controller, action, keys) + ] end end end @@ -460,7 +478,7 @@ module ActionController merged = options if expire_on[:controller] action = merged[:action] || 'index' - routes_by_controller[controller][action][merged.keys] + routes_by_controller[controller][action][merged.keys][1] end def routes_for_controller_and_action(controller, action) @@ -472,6 +490,12 @@ module ActionController end def routes_for_controller_and_action_and_keys(controller, action, keys) + routes.select do |route| + route.matches_controller_and_action? controller, action + end + end + + def deprecated_routes_for_controller_and_action_and_keys(controller, action, keys) selected = routes.select do |route| route.matches_controller_and_action? controller, action end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 6dcb67e..223f5f7 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -52,7 +52,7 @@ class PageCachingTest < ActionController::TestCase ActionController::Base.perform_caching = true ActionController::Routing::Routes.draw do |map| - map.main '', :controller => 'posts' + map.main '', :controller => 'posts', :format => nil map.formatted_posts 'posts.:format', :controller => 'posts' map.resources :posts map.connect ':controller/:action/:id' diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index f10e5a9..aab7f03 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1610,7 +1610,7 @@ class RouteTest < Test::Unit::TestCase end end -class RouteSetTest < Test::Unit::TestCase +class RouteSetTest < ActiveSupport::TestCase def set @set ||= ROUTING::RouteSet.new end @@ -2191,8 +2191,10 @@ class RouteSetTest < Test::Unit::TestCase map.connect "/ws/people", :controller => "people", :action => "index", :ws => true end - url = set.generate(:controller => "people", :action => "index", :ws => true) - assert_equal "/ws/people", url + assert_deprecated { + url = set.generate(:controller => "people", :action => "index", :ws => true) + assert_equal "/ws/people", url + } end def test_generate_changes_controller_module diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb index 863f841..0e149cf 100644 --- a/actionpack/test/controller/url_rewriter_test.rb +++ b/actionpack/test/controller/url_rewriter_test.rb @@ -304,7 +304,7 @@ class UrlWriterTests < ActionController::TestCase def test_named_routes_with_nil_keys ActionController::Routing::Routes.clear! ActionController::Routing::Routes.draw do |map| - map.main '', :controller => 'posts' + map.main '', :controller => 'posts', :format => nil map.resources :posts map.connect ':controller/:action/:id' end @@ -314,9 +314,9 @@ class UrlWriterTests < ActionController::TestCase controller = kls.new params = {:action => :index, :controller => :posts, :format => :xml} - assert_equal("http://www.basecamphq.com/posts.xml", controller.send(:url_for, params)) + assert_equal("http://www.basecamphq.com/posts.xml", controller.send(:url_for, params)) params[:format] = nil - assert_equal("http://www.basecamphq.com/", controller.send(:url_for, params)) + assert_equal("http://www.basecamphq.com/", controller.send(:url_for, params)) ensure ActionController::Routing::Routes.load! end -- 1.6.4.2 From 8a49af31581095a35b6dc8b20ed12c2f9fb80855 Mon Sep 17 00:00:00 2001 From: Jatinder Singh Date: Sun, 9 Aug 2009 20:43:56 -0700 Subject: [PATCH 226/779] AR should respect default values for MySQL BINARY and VARBINARY columns. [#1273 state:committed] Signed-off-by: Jeremy Kemper --- .../connection_adapters/mysql_adapter.rb | 4 +- activerecord/test/cases/column_definition_test.rb | 34 ++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 3c5e4f5..a568f65 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -75,7 +75,7 @@ module ActiveRecord module ConnectionAdapters class MysqlColumn < Column #:nodoc: def extract_default(default) - if type == :binary || type == :text + if sql_type =~ /blob/i || type == :text if default.blank? return null ? nil : '' else @@ -89,7 +89,7 @@ module ActiveRecord end def has_default? - return false if type == :binary || type == :text #mysql forbids defaults on blob and text columns + return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns super end diff --git a/activerecord/test/cases/column_definition_test.rb b/activerecord/test/cases/column_definition_test.rb index 98abc8e..fc9a0ac 100644 --- a/activerecord/test/cases/column_definition_test.rb +++ b/activerecord/test/cases/column_definition_test.rb @@ -33,4 +33,38 @@ class ColumnDefinitionTest < ActiveRecord::TestCase column.limit, column.precision, column.scale, column.default, column.null) assert_equal %Q{title varchar(20) DEFAULT 'Hello' NOT NULL}, column_def.to_sql end + + if current_adapter?(:MysqlAdapter) + def test_should_set_default_for_mysql_binary_data_types + binary_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", "a", "binary(1)") + assert_equal "a", binary_column.default + + varbinary_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", "a", "varbinary(1)") + assert_equal "a", varbinary_column.default + end + + def test_should_not_set_default_for_blob_and_text_data_types + assert_raise ArgumentError do + ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", "a", "blob") + end + + assert_raise ArgumentError do + ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", "Hello", "text") + end + + text_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", nil, "text") + assert_equal nil, text_column.default + + not_null_text_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", nil, "text", false) + assert_equal "", not_null_text_column.default + end + + def test_has_default_should_return_false_for_blog_and_test_data_types + blob_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", nil, "blob") + assert !blob_column.has_default? + + text_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", nil, "text") + assert !text_column.has_default? + end + end end -- 1.6.4.2 From 9bc80f4dd149ee70aa05c352bdd3fec46d22871a Mon Sep 17 00:00:00 2001 From: Gabe da Silveira Date: Sun, 9 Aug 2009 13:16:57 -0700 Subject: [PATCH 227/779] Fix that counter_cache breaks with has_many :dependent => :nullify. [#1196 state:committed] Signed-off-by: Jeremy Kemper --- .../associations/has_many_association.rb | 1 + .../associations/has_many_associations_test.rb | 25 ++++++++++++++++++++ .../test/cases/associations/join_model_test.rb | 6 ++-- activerecord/test/fixtures/posts.yml | 3 ++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index a2cbabf..1f673ad 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -74,6 +74,7 @@ module ActiveRecord "#{@reflection.primary_key_name} = NULL", "#{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})" ) + @owner.class.update_counters(@owner.id, cached_counter_attribute_name => -records.size) if has_cached_counter? end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 9a5758f..636fa48 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -508,6 +508,23 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 0, new_firm.clients_of_firm.size end + def test_deleting_updates_counter_cache + topic = Topic.first + assert_equal topic.replies.to_a.size, topic.replies_count + + topic.replies.delete(topic.replies.first) + topic.reload + assert_equal topic.replies.to_a.size, topic.replies_count + end + + def test_deleting_updates_counter_cache_without_dependent_destroy + post = posts(:welcome) + + assert_difference "post.reload.taggings_count", -1 do + post.taggings.delete(post.taggings.first) + end + end + def test_deleting_a_collection force_signal37_to_load_all_clients_of_firm companies(:first_firm).clients_of_firm.create("name" => "Another Client") @@ -553,6 +570,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end end + def test_clearing_updates_counter_cache + topic = Topic.first + + topic.replies.clear + topic.reload + assert_equal 0, topic.replies_count + end + def test_clearing_a_dependent_association_collection firm = companies(:first_firm) client_id = firm.dependent_clients_of_firm.first.id diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index d478180..0a8aa18 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -317,11 +317,11 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_belongs_to_polymorphic_with_counter_cache - assert_equal 0, posts(:welcome)[:taggings_count] + assert_equal 1, posts(:welcome)[:taggings_count] tagging = posts(:welcome).taggings.create(:tag => tags(:general)) - assert_equal 1, posts(:welcome, :reload)[:taggings_count] + assert_equal 2, posts(:welcome, :reload)[:taggings_count] tagging.destroy - assert posts(:welcome, :reload)[:taggings_count].zero? + assert_equal 1, posts(:welcome, :reload)[:taggings_count] end def test_unavailable_through_reflection diff --git a/activerecord/test/fixtures/posts.yml b/activerecord/test/fixtures/posts.yml index 92e5d19..f817493 100644 --- a/activerecord/test/fixtures/posts.yml +++ b/activerecord/test/fixtures/posts.yml @@ -4,6 +4,7 @@ welcome: title: Welcome to the weblog body: Such a lovely day comments_count: 2 + taggings_count: 1 type: Post thinking: @@ -11,6 +12,8 @@ thinking: author_id: 1 title: So I was thinking body: Like I hopefully always am + comments_count: 1 + taggings_count: 1 type: SpecialPost authorless: -- 1.6.4.2 From 011baa0f652b055e7e60ab2d94a15aeb907b68bb Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 9 Aug 2009 21:30:55 -0700 Subject: [PATCH 228/779] Fix test dependency on taggings --- .../associations/has_many_associations_test.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 636fa48..23a1071 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -10,11 +10,12 @@ require 'models/author' require 'models/comment' require 'models/person' require 'models/reader' +require 'models/tagging' class HasManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :categories, :companies, :developers, :projects, :developers_projects, :topics, :authors, :comments, :author_addresses, - :people, :posts, :readers + :people, :posts, :readers, :taggings def setup Client.destroyed_client_ids.clear -- 1.6.4.2 From d0bdff0799a4e6c6752fca56e69fd8b529117768 Mon Sep 17 00:00:00 2001 From: Morgan Schweers Date: Wed, 20 Aug 2008 11:32:20 -0700 Subject: [PATCH 229/779] Fix that creating a table whose primary key prefix type is :table_name generates an incorrectly pluralized primary key. [#872 state:committed] Signed-off-by: Jeremy Kemper --- .../abstract/schema_statements.rb | 2 +- activerecord/test/cases/migration_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index c29c156..02bb1e9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -99,7 +99,7 @@ module ActiveRecord # See also TableDefinition#column for details on how to create columns. def create_table(table_name, options = {}) table_definition = TableDefinition.new(self) - table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name)) unless options[:id] == false + table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false yield table_definition diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 16861f2..b305269 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -224,7 +224,7 @@ if ActiveRecord::Base.connection.supports_migrations? t.column :foo, :string end - assert_equal %w(foo testings_id), Person.connection.columns(:testings).map { |c| c.name }.sort + assert_equal %w(foo testing_id), Person.connection.columns(:testings).map { |c| c.name }.sort ensure Person.connection.drop_table :testings rescue nil ActiveRecord::Base.primary_key_prefix_type = nil @@ -237,7 +237,7 @@ if ActiveRecord::Base.connection.supports_migrations? t.column :foo, :string end - assert_equal %w(foo testingsid), Person.connection.columns(:testings).map { |c| c.name }.sort + assert_equal %w(foo testingid), Person.connection.columns(:testings).map { |c| c.name }.sort ensure Person.connection.drop_table :testings rescue nil ActiveRecord::Base.primary_key_prefix_type = nil -- 1.6.4.2 From e972acc0d7b63e5ce15bed69f41214fe741a7c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 24 Jul 2009 00:05:57 +0200 Subject: [PATCH 230/779] Allow radio buttons to work with booleans. [#2937 state:committed] Signed-off-by: Pratik Naik --- actionpack/lib/action_view/helpers/form_helper.rb | 4 ++-- actionpack/test/template/form_helper_test.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 8efa9a0..db7de8e 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -861,8 +861,8 @@ module ActionView private def add_default_name_and_id_for_value(tag_value, options) - if tag_value - pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase + unless tag_value.nil? + pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase specified_id = options["id"] add_default_name_and_id(options) options["id"] += "_#{pretty_tag_value}" unless specified_id diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 5756456..a32cdbe 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -311,6 +311,16 @@ class FormHelperTest < ActionView::TestCase ) end + def test_radio_button_with_booleans + assert_dom_equal('', + radio_button("post", "secret", true) + ) + + assert_dom_equal('', + radio_button("post", "secret", false) + ) + end + def test_text_area assert_dom_equal( '', -- 1.6.4.2 From 9a3a7983c3f2998bcd9bb53944c61ef2406e7c59 Mon Sep 17 00:00:00 2001 From: Jaime Bellmyer Date: Thu, 26 Feb 2009 09:48:58 -0600 Subject: [PATCH 231/779] raises exception (ActiveRecord::ConfigurationError with message) on habtm association creation if join table contains a primary key Signed-off-by: Jeremy Kemper --- .../has_and_belongs_to_many_association.rb | 4 ++ .../connection_adapters/abstract_adapter.rb | 7 +++ .../connection_adapters/mysql_adapter.rb | 10 ++++ .../connection_adapters/postgresql_adapter.rb | 11 +++++ .../connection_adapters/sqlite_adapter.rb | 4 ++ .../cases/associations/habtm_join_table_test.rb | 45 ++++++++++++++++++++ activerecord/test/cases/pk_test.rb | 18 ++++++++ 7 files changed, 99 insertions(+), 0 deletions(-) create mode 100644 activerecord/test/cases/associations/habtm_join_table_test.rb diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index af9ce3d..834f339 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -29,6 +29,10 @@ module ActiveRecord end def insert_record(record, force = true, validate = true) + if ActiveRecord::Base.connection.supports_primary_key? && ActiveRecord::Base.connection.primary_key(@reflection.options[:join_table]) + raise ActiveRecord::ConfigurationError, "Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})." + end + if record.new_record? if force record.save! diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index a8cd9f0..22871f2 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -54,6 +54,13 @@ module ActiveRecord false end + # Can this adapter determine the primary key for tables not attached + # to an ActiveRecord class, such as join tables? Backend specific, as + # the abstract adapter always returns +false+. + def supports_primary_key? + false + end + # Does this adapter support using DISTINCT within COUNT? This is +true+ # for all adapters except sqlite. def supports_count_distinct? diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index a568f65..1259278 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -207,6 +207,10 @@ module ActiveRecord true end + def supports_primary_key? #:nodoc: + true + end + def supports_savepoints? #:nodoc: true end @@ -549,6 +553,12 @@ module ActiveRecord keys.length == 1 ? [keys.first, nil] : nil end + # Returns just a table's primary key + def primary_key(table) + pk_and_sequence = pk_and_sequence_for(table) + pk_and_sequence && pk_and_sequence.first + end + def case_sensitive_equality_operator "= BINARY" end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 8440223..b780d74 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -249,6 +249,11 @@ module ActiveRecord true end + # Does PostgreSQL support finding primary key on non-ActiveRecord tables? + def supports_primary_key? #:nodoc: + true + end + # Does PostgreSQL support standard conforming strings? def supports_standard_conforming_strings? # Temporarily set the client message level above error to prevent unintentional @@ -810,6 +815,12 @@ module ActiveRecord nil end + # Returns just a table's primary key + def primary_key(table) + pk_and_sequence = pk_and_sequence_for(table) + pk_and_sequence && pk_and_sequence.first + end + # Renames a table. def rename_table(name, new_name) execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}" diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 250318f..9f8075b 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -108,6 +108,10 @@ module ActiveRecord true end + def supports_primary_key? #:nodoc: + true + end + def requires_reloading? true end diff --git a/activerecord/test/cases/associations/habtm_join_table_test.rb b/activerecord/test/cases/associations/habtm_join_table_test.rb new file mode 100644 index 0000000..0586df1 --- /dev/null +++ b/activerecord/test/cases/associations/habtm_join_table_test.rb @@ -0,0 +1,45 @@ +require 'cases/helper' + +class MyReader < ActiveRecord::Base + has_and_belongs_to_many :my_books +end + +class MyBook < ActiveRecord::Base + has_and_belongs_to_many :my_readers +end + +class JoinTableTest < ActiveRecord::TestCase + def setup + ActiveRecord::Base.connection.create_table :my_books, :force => true do |t| + t.string :name + end + assert ActiveRecord::Base.connection.table_exists?(:my_books) + + ActiveRecord::Base.connection.create_table :my_readers, :force => true do |t| + t.string :name + end + assert ActiveRecord::Base.connection.table_exists?(:my_readers) + + ActiveRecord::Base.connection.create_table :my_books_my_readers, :force => true do |t| + t.integer :my_book_id + t.integer :my_reader_id + end + assert ActiveRecord::Base.connection.table_exists?(:my_books_my_readers) + end + + def teardown + ActiveRecord::Base.connection.drop_table :my_books + ActiveRecord::Base.connection.drop_table :my_readers + ActiveRecord::Base.connection.drop_table :my_books_my_readers + end + + uses_transaction :test_should_raise_exception_when_join_table_has_a_primary_key + def test_should_raise_exception_when_join_table_has_a_primary_key + if ActiveRecord::Base.connection.supports_primary_key? + assert_raise ActiveRecord::ConfigurationError do + jaime = MyReader.create(:name=>"Jaime") + jaime.my_books << MyBook.create(:name=>'Great Expectations') + end + end + end +end diff --git a/activerecord/test/cases/pk_test.rb b/activerecord/test/cases/pk_test.rb index 948a570..c121e0a 100644 --- a/activerecord/test/cases/pk_test.rb +++ b/activerecord/test/cases/pk_test.rb @@ -98,4 +98,22 @@ class PrimaryKeysTest < ActiveRecord::TestCase def test_instance_destroy_should_quote_pkey assert_nothing_raised { MixedCaseMonkey.find(1).destroy } end + + def test_supports_primary_key + assert_nothing_raised NoMethodError do + ActiveRecord::Base.connection.supports_primary_key? + end + end + + def test_primary_key_returns_value_if_it_exists + if ActiveRecord::Base.connection.supports_primary_key? + assert_equal 'id', ActiveRecord::Base.connection.primary_key('developers') + end + end + + def test_primary_key_returns_nil_if_it_does_not_exist + if ActiveRecord::Base.connection.supports_primary_key? + assert_nil ActiveRecord::Base.connection.primary_key('developers_projects') + end + end end -- 1.6.4.2 From 9d51f6286680b832b0df5e3ce288575214c1de59 Mon Sep 17 00:00:00 2001 From: Jaime Bellmyer Date: Mon, 9 Mar 2009 13:23:27 -0500 Subject: [PATCH 232/779] raises an exception on habtm join table inserts if join table contains a primary key. Caches this check to save time on subsequent inserts. [#2086 state:committed] Signed-off-by: Jeremy Kemper --- .../has_and_belongs_to_many_association.rb | 16 ++++++++++++++-- .../cases/associations/habtm_join_table_test.rb | 13 ++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 834f339..cb60baa 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -1,6 +1,11 @@ module ActiveRecord module Associations class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc: + def initialize(owner, reflection) + super + @primary_key_list = {} + end + def create(attributes = {}) create_record(attributes) { |record| insert_record(record) } end @@ -17,6 +22,12 @@ module ActiveRecord @reflection.reset_column_information end + def has_primary_key? + return @has_primary_key unless @has_primary_key.nil? + @has_primary_key = (ActiveRecord::Base.connection.supports_primary_key? && + ActiveRecord::Base.connection.primary_key(@reflection.options[:join_table])) + end + protected def construct_find_options!(options) options[:joins] = @join_sql @@ -29,8 +40,9 @@ module ActiveRecord end def insert_record(record, force = true, validate = true) - if ActiveRecord::Base.connection.supports_primary_key? && ActiveRecord::Base.connection.primary_key(@reflection.options[:join_table]) - raise ActiveRecord::ConfigurationError, "Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})." + if has_primary_key? + raise ActiveRecord::ConfigurationError, + "Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})." end if record.new_record? diff --git a/activerecord/test/cases/associations/habtm_join_table_test.rb b/activerecord/test/cases/associations/habtm_join_table_test.rb index 0586df1..bf3e04c 100644 --- a/activerecord/test/cases/associations/habtm_join_table_test.rb +++ b/activerecord/test/cases/associations/habtm_join_table_test.rb @@ -8,7 +8,7 @@ class MyBook < ActiveRecord::Base has_and_belongs_to_many :my_readers end -class JoinTableTest < ActiveRecord::TestCase +class HabtmJoinTableTest < ActiveRecord::TestCase def setup ActiveRecord::Base.connection.create_table :my_books, :force => true do |t| t.string :name @@ -42,4 +42,15 @@ class JoinTableTest < ActiveRecord::TestCase end end end + + uses_transaction :test_should_cache_result_of_primary_key_check + def test_should_cache_result_of_primary_key_check + if ActiveRecord::Base.connection.supports_primary_key? + ActiveRecord::Base.connection.stubs(:primary_key).with('my_books_my_readers').returns(false).once + weaz = MyReader.create(:name=>'Weaz') + + weaz.my_books << MyBook.create(:name=>'Great Expectations') + weaz.my_books << MyBook.create(:name=>'Greater Expectations') + end + end end -- 1.6.4.2 From 5c74cffae66be19418e7d99e08eae0c5beeb3134 Mon Sep 17 00:00:00 2001 From: Leonardo Borges Date: Sun, 9 Aug 2009 12:56:25 +0400 Subject: [PATCH 233/779] PostgreSQL: XML datatype support [#1874 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/CHANGELOG | 2 + .../abstract/schema_definitions.rb | 15 +++++++++ .../connection_adapters/postgresql_adapter.rb | 34 ++++++++++++------- activerecord/test/cases/migration_test.rb | 28 +++++++++++----- activerecord/test/cases/schema_dumper_test.rb | 12 ++++++- .../test/schema/postgresql_specific_schema.rb | 15 +++++++- 6 files changed, 81 insertions(+), 25 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 735ba0b..bc53d5d 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *2.3.4 (pending)* +* PostgreSQL: XML datatype support. #1874 [Leonardo Borges] + * SQLite: deprecate the 'dbfile' option in favor of 'database.' #2363 [Paul Hinze, Jeremy Kemper] diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index f346e3e..520f3c8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -315,6 +315,20 @@ module ActiveRecord @base = base end + #Handles non supported datatypes - e.g. XML + def method_missing(symbol, *args) + if symbol.to_s == 'xml' + xml_column_fallback(args) + end + end + + def xml_column_fallback(*args) + case @base.adapter_name.downcase + when 'sqlite', 'mysql' + options = args.extract_options! + column(args[0], :text, options) + end + end # Appends a primary key definition to the table definition. # Can be called multiple times, but this is probably not a good idea. def primary_key(name) @@ -705,3 +719,4 @@ module ActiveRecord end end + diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index b780d74..bc289ff 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -39,6 +39,12 @@ module ActiveRecord end module ConnectionAdapters + class TableDefinition + def xml(*args) + options = args.extract_options! + column(args[0], 'xml', options) + end + end # PostgreSQL-specific extensions to column definitions in a table. class PostgreSQLColumn < Column #:nodoc: # Instantiates a new PostgreSQL column definition in a table. @@ -67,7 +73,7 @@ module ActiveRecord # depending on the server specifics super end - + # Maps PostgreSQL-specific data types to logical Rails types. def simplified_type(field_type) case field_type @@ -99,10 +105,10 @@ module ActiveRecord :string # XML type when /^xml$/ - :string + :xml # Arrays when /^\D+\[\]$/ - :string + :string # Object identifier types when /^oid$/ :integer @@ -111,7 +117,7 @@ module ActiveRecord super end end - + # Extracts the value from a PostgreSQL column default definition. def self.extract_value_from_default(default) case default @@ -194,7 +200,8 @@ module ActiveRecord :time => { :name => "time" }, :date => { :name => "date" }, :binary => { :name => "bytea" }, - :boolean => { :name => "boolean" } + :boolean => { :name => "boolean" }, + :xml => { :name => "xml" } } # Returns 'PostgreSQL' as adapter name for identification purposes. @@ -277,7 +284,7 @@ module ActiveRecord def supports_ddl_transactions? true end - + def supports_savepoints? true end @@ -369,7 +376,7 @@ module ActiveRecord if value.kind_of?(String) && column && column.type == :binary "#{quoted_string_prefix}'#{escape_bytea(value)}'" elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/ - "xml '#{quote_string(value)}'" + "xml E'#{quote_string(value)}'" elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/ # Not truly string input, so doesn't require (or allow) escape string syntax. "'#{value.to_s}'" @@ -568,7 +575,7 @@ module ActiveRecord def rollback_db_transaction execute "ROLLBACK" end - + if defined?(PGconn::PQTRANS_IDLE) # The ruby-pg driver supports inspecting the transaction status, # while the ruby-postgres driver does not. @@ -919,18 +926,18 @@ module ActiveRecord sql = "DISTINCT ON (#{columns}) #{columns}, " sql << order_columns * ', ' end - + # Returns an ORDER BY clause for the passed order option. - # + # # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this # by wrapping the +sql+ string as a sub-select and ordering in that query. def add_order_by_for_association_limiting!(sql, options) #:nodoc: return sql if options[:order].blank? - + order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?) order.map! { |s| 'DESC' if s =~ /\bdesc$/i } order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ') - + sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}" end @@ -1043,7 +1050,7 @@ module ActiveRecord if res.ftype(cell_index) == MONEY_COLUMN_TYPE_OID # Because money output is formatted according to the locale, there are two # cases to consider (note the decimal separators): - # (1) $12,345,678.12 + # (1) $12,345,678.12 # (2) $12.345.678,12 case column = row[cell_index] when /^-?\D+[\d,]+\.\d{2}$/ # (1) @@ -1103,3 +1110,4 @@ module ActiveRecord end end end + diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index b305269..db3f9fb 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -389,7 +389,7 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal 9, wealth_column.precision assert_equal 7, wealth_column.scale end - + def test_native_types Person.delete_all Person.connection.add_column "people", "last_name", :string @@ -921,9 +921,9 @@ if ActiveRecord::Base.connection.supports_migrations? def test_migrator_one_down ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid") - + ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1) - + Person.reset_column_information assert Person.column_methods_hash.include?(:last_name) assert !Reminder.table_exists? @@ -1059,20 +1059,20 @@ if ActiveRecord::Base.connection.supports_migrations? assert Reminder.create("content" => "hello world", "remind_at" => Time.now) assert_equal "hello world", Reminder.find(:first).content end - + def test_migrator_rollback ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid") assert_equal(3, ActiveRecord::Migrator.current_version) - + ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid") assert_equal(2, ActiveRecord::Migrator.current_version) - + ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid") assert_equal(1, ActiveRecord::Migrator.current_version) - + ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid") assert_equal(0, ActiveRecord::Migrator.current_version) - + ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid") assert_equal(0, ActiveRecord::Migrator.current_version) end @@ -1224,7 +1224,7 @@ if ActiveRecord::Base.connection.supports_migrations? end end - + class SexyMigrationsTest < ActiveRecord::TestCase def test_references_column_type_adds_id with_new_table do |t| @@ -1280,6 +1280,15 @@ if ActiveRecord::Base.connection.supports_migrations? end end + if current_adapter?(:PostgreSQLAdapter) + def test_xml_creates_xml_column + with_new_table do |t| + t.expects(:column).with(:data, 'xml', {}) + t.xml :data + end + end + end + protected def with_new_table Person.connection.create_table :delete_me, :force => true do |t| @@ -1493,3 +1502,4 @@ if ActiveRecord::Base.connection.supports_migrations? end end end + diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 0d25bb7..ba714a9 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -156,7 +156,7 @@ class SchemaDumperTest < ActiveRecord::TestCase index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip assert_equal 'add_index "companies", ["firm_id", "type", "rating", "ruby_type"], :name => "company_index"', index_definition end - + def test_schema_dump_should_honor_nonstandard_primary_keys output = standard_dump match = output.match(%r{create_table "movies"(.*)do}) @@ -191,6 +191,15 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r{:precision => 3,[[:space:]]+:scale => 2,[[:space:]]+:default => 2.78}, output end + if current_adapter?(:PostgreSQLAdapter) + def test_schema_dump_includes_xml_shorthand_definition + output = standard_dump + if %r{create_table "postgresql_xml_data_type"} =~ output + assert_match %r{t.xml "data"}, output + end + end + end + def test_schema_dump_keeps_id_column_when_id_is_false_and_id_column_added output = standard_dump match = output.match(%r{create_table "goofy_string_id"(.*)do.*\n(.*)\n}) @@ -199,3 +208,4 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_match %r{t.string[[:space:]]+"id",[[:space:]]+:null => false$}, match[2], "non-primary key id column not preserved" end end + diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb index 576a4d0..3d8911b 100644 --- a/activerecord/test/schema/postgresql_specific_schema.rb +++ b/activerecord/test/schema/postgresql_specific_schema.rb @@ -1,7 +1,7 @@ ActiveRecord::Schema.define do %w(postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_bit_strings - postgresql_oids defaults geometrics).each do |table_name| + postgresql_oids postgresql_xml_data_type defaults geometrics).each do |table_name| execute "DROP TABLE IF EXISTS #{quote_table_name table_name}" end @@ -100,4 +100,15 @@ _SQL obj_id OID ); _SQL -end \ No newline at end of file + + begin + execute <<_SQL + CREATE TABLE postgresql_xml_data_type ( + id SERIAL PRIMARY KEY, + data xml + ); +_SQL +rescue #This version of PostgreSQL either has no XML support or is was not compiled with XML support: skipping table + end +end + -- 1.6.4.2 From 9341655fa30ffc245093493422c9abe72eeb64ed Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Sun, 9 Aug 2009 11:27:17 +0400 Subject: [PATCH 234/779] Fix that JSON parser fails to read escaped backslashes. [#973 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/active_support/json/backends/yaml.rb | 7 +++++-- activesupport/test/json/decoding_test.rb | 10 ++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb index a2797cc..8c5b697 100644 --- a/activesupport/lib/active_support/json/backends/yaml.rb +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -17,7 +17,7 @@ module ActiveSupport rescue ArgumentError => e raise ParseError, "Invalid JSON string" end - + protected # Ensure that ":" and "," are always followed by a space def convert_json_to_yaml(json) #:nodoc: @@ -41,6 +41,8 @@ module ActiveSupport end when ":","," marks << scanner.pos - 1 unless quoting + when "\\" + scanner.skip(/\\/) end end @@ -82,4 +84,5 @@ module ActiveSupport end end end -end \ No newline at end of file +end + diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index 86053c9..d0d0ec7 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -12,10 +12,10 @@ class TestJSONDecoding < ActiveSupport::TestCase %({"a": "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"}, # multibyte %({"matzue": "松江", "asakusa": "浅草"}) => {"matzue" => "松江", "asakusa" => "浅草"}, - %({"a": "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)}, - %({"a": "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)}, + %({"a": "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)}, + %({"a": "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)}, # no time zone - %({"a": "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"}, + %({"a": "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"}, # needs to be *exact* %({"a": " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "}, %({"a": "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"}, @@ -27,6 +27,7 @@ class TestJSONDecoding < ActiveSupport::TestCase %({"a": null}) => {"a" => nil}, %({"a": true}) => {"a" => true}, %({"a": false}) => {"a" => false}, + %q({"bad":"\\\\","trailing":""}) => {"bad" => "\\", "trailing" => ""}, %q({"a": "http:\/\/test.host\/posts\/1"}) => {"a" => "http://test.host/posts/1"}, %q({"a": "\u003cunicode\u0020escape\u003e"}) => {"a" => ""}, %q({"a": "\\\\u0020skip double backslashes"}) => {"a" => "\\u0020skip double backslashes"}, @@ -74,4 +75,5 @@ class TestJSONDecoding < ActiveSupport::TestCase def test_failed_json_decoding assert_raise(ActiveSupport::JSON::ParseError) { ActiveSupport::JSON.decode(%({: 1})) } end -end \ No newline at end of file +end + -- 1.6.4.2 From c3da22c042d083b788fd35ce7a25e3d8933bcae6 Mon Sep 17 00:00:00 2001 From: Fabien Jakimowicz Date: Sun, 9 Aug 2009 15:56:23 +0200 Subject: [PATCH 235/779] Add support for errors in JSON format. [#1956 state:committed] Signed-off-by: Jeremy Kemper --- activeresource/CHANGELOG | 2 + activeresource/lib/active_resource/base.rb | 6 ++- activeresource/lib/active_resource/validations.rb | 24 ++++++- activeresource/test/base_errors_test.rb | 75 +++++++++++++++----- 4 files changed, 83 insertions(+), 24 deletions(-) diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index fcd8634..bb38073 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,5 +1,7 @@ *2.3.4 (pending)* +* Add support for errors in JSON format. #1956 [Fabien Jakimowicz] + * Recognizes 410 as Resource Gone. #2316 [Jordan Brough, Jatinder Singh] * More thorough SSL support. #2370 [Roy Nicholson] diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index b176e61..e25002b 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -174,7 +174,7 @@ module ActiveResource # # Active Resource supports validations on resources and will return errors if any these validations fail # (e.g., "First name can not be blank" and so on). These types of errors are denoted in the response by - # a response code of 422 and an XML representation of the validation errors. The save operation will + # a response code of 422 and an XML or JSON representation of the validation errors. The save operation will # then fail (with a false return value) and the validation errors can be accessed on the resource in question. # # ryan = Person.find(1) @@ -183,10 +183,14 @@ module ActiveResource # # # When # # PUT http://api.people.com:3000/people/1.xml + # # or + # # PUT http://api.people.com:3000/people/1.json # # is requested with invalid values, the response is: # # # # Response (422): # # First cannot be empty + # # or + # # {"errors":["First cannot be empty"]} # # # # ryan.errors.invalid?(:first) # => true diff --git a/activeresource/lib/active_resource/validations.rb b/activeresource/lib/active_resource/validations.rb index de33399..ee377ac 100644 --- a/activeresource/lib/active_resource/validations.rb +++ b/activeresource/lib/active_resource/validations.rb @@ -199,11 +199,10 @@ module ActiveResource alias_method :count, :size alias_method :length, :size - # Grabs errors from the XML response. - def from_xml(xml) + # Grabs errors from an array of messages (like ActiveRecord::Validations) + def from_array(messages) clear humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) } - messages = Array.wrap(Hash.from_xml(xml)['errors']['error']) rescue [] messages.each do |message| attr_message = humanized_attributes.keys.detect do |attr_name| if message[0, attr_name.size + 1] == "#{attr_name} " @@ -214,6 +213,18 @@ module ActiveResource add_to_base message if attr_message.nil? end end + + # Grabs errors from the json response. + def from_json(json) + array = ActiveSupport::JSON.decode(json)['errors'] rescue [] + from_array array + end + + # Grabs errors from the XML response. + def from_xml(xml) + array = Array.wrap(Hash.from_xml(xml)['errors']['error']) rescue [] + from_array array + end end # Module to support validation and errors with Active Resource objects. The module overrides @@ -248,7 +259,12 @@ module ActiveResource save_without_validation true rescue ResourceInvalid => error - errors.from_xml(error.response.body) + case error.response['Content-Type'] + when 'application/xml' + errors.from_xml(error.response.body) + when 'application/json' + errors.from_json(error.response.body) + end false end diff --git a/activeresource/test/base_errors_test.rb b/activeresource/test/base_errors_test.rb index 7ae92c7..c1b4867 100644 --- a/activeresource/test/base_errors_test.rb +++ b/activeresource/test/base_errors_test.rb @@ -4,45 +4,82 @@ require "fixtures/person" class BaseErrorsTest < Test::Unit::TestCase def setup ActiveResource::HttpMock.respond_to do |mock| - mock.post "/people.xml", {}, "Age can't be blankName can't be blankName must start with a letterPerson quota full for today.", 422 + mock.post "/people.xml", {}, %q(Age can't be blankName can't be blankName must start with a letterPerson quota full for today.), 422, {'Content-Type' => 'application/xml'} + mock.post "/people.json", {}, %q({"errors":["Age can't be blank","Name can't be blank","Name must start with a letter","Person quota full for today."]}), 422, {'Content-Type' => 'application/json'} end @person = Person.new(:name => '', :age => '') assert_equal @person.save, false end def test_should_mark_as_invalid - assert !@person.valid? + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + assert !@person.valid? + end + end end def test_should_parse_xml_errors - assert_kind_of ActiveResource::Errors, @person.errors - assert_equal 4, @person.errors.size + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + assert_kind_of ActiveResource::Errors, @person.errors + assert_equal 4, @person.errors.size + end + end end def test_should_parse_errors_to_individual_attributes - assert @person.errors.invalid?(:name) - assert_equal "can't be blank", @person.errors.on(:age) - assert_equal ["can't be blank", "must start with a letter"], @person.errors[:name] - assert_equal "Person quota full for today.", @person.errors.on_base + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + assert @person.errors[:name].any? + assert_equal "can't be blank", @person.errors[:age] + assert_equal ["can't be blank", "must start with a letter"], @person.errors[:name] + assert_equal "Person quota full for today.", @person.errors[:base] + end + end end def test_should_iterate_over_errors - errors = [] - @person.errors.each { |attribute, message| errors << [attribute, message] } - assert errors.include?(["name", "can't be blank"]) + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + errors = [] + @person.errors.each { |attribute, message| errors << [attribute, message] } + assert errors.include?(['name', "can't be blank"]) + end + end end def test_should_iterate_over_full_errors - errors = [] - @person.errors.each_full { |message| errors << message } - assert errors.include?("Name can't be blank") + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + errors = [] + @person.errors.to_a.each { |message| errors << message } + assert errors.include?(["name", "can't be blank"]) + end + end end def test_should_format_full_errors - full = @person.errors.full_messages - assert full.include?("Age can't be blank") - assert full.include?("Name can't be blank") - assert full.include?("Name must start with a letter") - assert full.include?("Person quota full for today.") + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + full = @person.errors.full_messages + assert full.include?("Age can't be blank") + assert full.include?("Name can't be blank") + assert full.include?("Name must start with a letter") + assert full.include?("Person quota full for today.") + end + end + end + + private + def invalid_user_using_format(mime_type_reference) + previous_format = Person.format + Person.format = mime_type_reference + @person = Person.new(:name => '', :age => '') + assert_equal false, @person.save + + yield + ensure + Person.format = previous_format end end -- 1.6.4.2 From 9e96f37edd598d698c9f39c9af114198e5000c70 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Wed, 10 Dec 2008 16:50:06 +1030 Subject: [PATCH 236/779] Use table prefix and suffix for schema_migrations index. [#1543 state:committed] Signed-off-by: Jeremy Kemper --- .../abstract/schema_statements.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 02bb1e9..7e82099 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -321,7 +321,7 @@ module ActiveRecord schema_migrations_table.column :version, :string, :null => false end add_index sm_table, :version, :unique => true, - :name => 'unique_schema_migrations' + :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}" # Backwards-compatibility: if we find schema_info, assume we've # migrated up to that point: -- 1.6.4.2 From 25fe43bc14c0803ca28ae9cdf965a4f85413166a Mon Sep 17 00:00:00 2001 From: Cristi Balan Date: Sun, 9 Aug 2009 14:05:56 +0300 Subject: [PATCH 237/779] Add tests for scoping schema_migrations index by global table prefix and suffix [#1543 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/test/cases/migration_test.rb | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index db3f9fb..3b6326d 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -25,6 +25,24 @@ if ActiveRecord::Base.connection.supports_migrations? end end + class MigrationTableAndIndexTest < ActiveRecord::TestCase + def test_add_schema_info_respects_prefix_and_suffix + conn = ActiveRecord::Base.connection + + conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) + ActiveRecord::Base.table_name_prefix = 'foo_' + ActiveRecord::Base.table_name_suffix = '_bar' + conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) + + conn.initialize_schema_migrations_table + + assert_equal "foo_unique_schema_migrations_bar", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name] + ensure + ActiveRecord::Base.table_name_prefix = "" + ActiveRecord::Base.table_name_suffix = "" + end + end + class MigrationTest < ActiveRecord::TestCase self.use_transactional_fixtures = false -- 1.6.4.2 From 4e014379a3c792281138c5a19a5bd74e9435fede Mon Sep 17 00:00:00 2001 From: Erik Ostrom Date: Sun, 9 Aug 2009 22:14:59 -0700 Subject: [PATCH 238/779] Correctly handle offsets in Multibyte::Chars#index and #rindex. The offset in codepoints was being passed directly to the wrapped string's index/rindex method. Now we translate the offset into bytes first. [#3028 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/active_support/multibyte/chars.rb | 17 ++++++++++++++++- activesupport/test/multibyte_chars_test.rb | 12 ++++++++++++ 2 files changed, 28 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 60f082b..3d392d2 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -205,7 +205,22 @@ module ActiveSupport #:nodoc: # 'Café périferôl'.mb_chars.index('ô') #=> 12 # 'Café périferôl'.mb_chars.index(/\w/u) #=> 0 def index(needle, offset=0) - index = @wrapped_string.index(needle, offset) + wrapped_offset = self.first(offset).wrapped_string.length + index = @wrapped_string.index(needle, wrapped_offset) + index ? (self.class.u_unpack(@wrapped_string.slice(0...index)).size) : nil + end + + # Returns the position _needle_ in the string, counting in + # codepoints, searching backward from _offset_ or the end of the + # string. Returns +nil+ if _needle_ isn't found. + # + # Example: + # 'Café périferôl'.mb_chars.rindex('é') #=> 6 + # 'Café périferôl'.mb_chars.rindex(/\w/u) #=> 13 + def rindex(needle, offset=nil) + offset ||= length + wrapped_offset = self.first(offset).wrapped_string.length + index = @wrapped_string.rindex(needle, wrapped_offset) index ? (self.class.u_unpack(@wrapped_string.slice(0...index)).size) : nil end diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 661b33c..05eee42 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -231,7 +231,19 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase assert_nil @chars.index('u') assert_equal 0, @chars.index('こに') assert_equal 2, @chars.index('ち') + assert_equal 2, @chars.index('ち', -2) + assert_equal nil, @chars.index('ち', -1) assert_equal 3, @chars.index('わ') + assert_equal 5, 'ééxééx'.mb_chars.index('x', 4) + end + + def test_rindex_should_return_character_offset + assert_nil @chars.rindex('u') + assert_equal 1, @chars.rindex('に') + assert_equal 2, @chars.rindex('ち', -2) + assert_nil @chars.rindex('ち', -3) + assert_equal 6, 'Café périferôl'.mb_chars.rindex('é') + assert_equal 13, 'Café périferôl'.mb_chars.rindex(/\w/u) end def test_indexed_insert_should_take_character_offsets -- 1.6.4.2 From 9a42096e958cbce805ad9b6f35c5f03fe4775855 Mon Sep 17 00:00:00 2001 From: codeape Date: Sun, 9 Aug 2009 19:18:01 -0700 Subject: [PATCH 239/779] Introduce grouped_collection_select helper. [#1249 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/CHANGELOG | 2 + .../lib/action_view/helpers/form_options_helper.rb | 67 ++++++++++++++++++++ .../test/template/form_options_helper_test.rb | 34 ++++++++++ 3 files changed, 103 insertions(+), 0 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index da3749f..f007672 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *2.3.4 (pending)* +* Introduce grouped_collection_select helper. #1249 [Dan Codeape, Erik Ostrom] + * Ruby 1.9: fix Content-Length for multibyte send_data streaming. #2661 [Sava Chankov] diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 6adbab1..597e242 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -162,6 +162,60 @@ module ActionView InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options) end + + # Returns + # + # + # + # + # + # + # + # + # + # + def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {}) + InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options) + end + + + # Return select and option tags for the given object and method, using # #time_zone_options_for_select to generate the list of option tags. # @@ -490,6 +544,15 @@ module ActionView ) end + def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options) + html_options = html_options.stringify_keys + add_default_name_and_id(html_options) + value = value(object) + content_tag( + "select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options + ) + end + def to_time_zone_select_tag(priority_zones, options, html_options) html_options = html_options.stringify_keys add_default_name_and_id(html_options) @@ -524,6 +587,10 @@ module ActionView @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)) end + def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {}) + @template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options)) + end + def time_zone_select(method, priority_zones = nil, options = {}, html_options = {}) @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options)) end diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 7362440..aa40e46 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -763,6 +763,40 @@ class FormOptionsHelperTest < ActionView::TestCase html end + def test_grouped_collection_select + @continents = [ + Continent.new("", [Country.new("", ""), Country.new("so", "Somalia")] ), + Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] ) + ] + + @post = Post.new + @post.origin = 'dk' + + assert_dom_equal( + %Q{}, + grouped_collection_select("post", "origin", @continents, :countries, :continent_name, :country_id, :country_name) + ) + end + + def test_grouped_collection_select_under_fields_for + @continents = [ + Continent.new("", [Country.new("", ""), Country.new("so", "Somalia")] ), + Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] ) + ] + + @post = Post.new + @post.origin = 'dk' + + fields_for :post, @post do |f| + concat f.grouped_collection_select("origin", @continents, :countries, :continent_name, :country_id, :country_name) + end + + assert_dom_equal( + %Q{}, + output_buffer + ) + end + private def dummy_posts -- 1.6.4.2 From 9284bcc35a6431a6044da2b608998b83e4fb1f9b Mon Sep 17 00:00:00 2001 From: Kamal Fariz Mahyuddin Date: Mon, 10 Aug 2009 14:12:26 +0800 Subject: [PATCH 240/779] find_cmd should return the full path of the db command [#1488 state:committed] Signed-off-by: Jeremy Kemper --- railties/lib/commands/dbconsole.rb | 12 ++++++++---- 1 files changed, 8 insertions(+), 4 deletions(-) diff --git a/railties/lib/commands/dbconsole.rb b/railties/lib/commands/dbconsole.rb index 8002264..e6f11a4 100644 --- a/railties/lib/commands/dbconsole.rb +++ b/railties/lib/commands/dbconsole.rb @@ -33,11 +33,15 @@ end def find_cmd(*commands) dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR) commands += commands.map{|cmd| "#{cmd}.exe"} if RUBY_PLATFORM =~ /win32/ - commands.detect do |cmd| - dirs_on_path.detect do |path| - File.executable? File.join(path, cmd) + + full_path_command = nil + found = commands.detect do |cmd| + dir = dirs_on_path.detect do |path| + full_path_command = File.join(path, cmd) + File.executable? full_path_command end - end || abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.") + end + found ? full_path_command : abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.") end case config["adapter"] -- 1.6.4.2 From a91969803e96b4f66cebd65ac67c8cdc4ca4fcef Mon Sep 17 00:00:00 2001 From: Hongli Lai (Phusion) Date: Mon, 10 Aug 2009 21:48:16 +0200 Subject: [PATCH 241/779] Correctly unlock the reloader lock if the underlying app raises an exception. [#2873 state:incomplete] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/reloader.rb | 33 +++++++++++++++----------- actionpack/test/controller/reloader_test.rb | 14 ++++++++++- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/actionpack/lib/action_controller/reloader.rb b/actionpack/lib/action_controller/reloader.rb index cfc6bfc..e47d5ce 100644 --- a/actionpack/lib/action_controller/reloader.rb +++ b/actionpack/lib/action_controller/reloader.rb @@ -34,20 +34,25 @@ module ActionController def call(env) @lock.lock Dispatcher.reload_application - status, headers, body = @app.call(env) - # We do not want to call 'cleanup_application' in an ensure block - # because the returned Rack response body may lazily generate its data. This - # is for example the case if one calls - # - # render :text => lambda { ... code here which refers to application models ... } - # - # in an ActionController. - # - # Instead, we will want to cleanup the application code after the request is - # completely finished. So we wrap the body in a BodyWrapper class so that - # when the Rack handler calls #close during the end of the request, we get to - # run our cleanup code. - [status, headers, BodyWrapper.new(body, @lock)] + begin + status, headers, body = @app.call(env) + # We do not want to call 'cleanup_application' in an ensure block + # because the returned Rack response body may lazily generate its data. This + # is for example the case if one calls + # + # render :text => lambda { ... code here which refers to application models ... } + # + # in an ActionController. + # + # Instead, we will want to cleanup the application code after the request is + # completely finished. So we wrap the body in a BodyWrapper class so that + # when the Rack handler calls #close during the end of the request, we get to + # run our cleanup code. + [status, headers, BodyWrapper.new(body, @lock)] + rescue Exception + @lock.unlock + raise + end end end end diff --git a/actionpack/test/controller/reloader_test.rb b/actionpack/test/controller/reloader_test.rb index 5aa5678..9eab9e1 100644 --- a/actionpack/test/controller/reloader_test.rb +++ b/actionpack/test/controller/reloader_test.rb @@ -66,7 +66,7 @@ class ReloaderTests < ActiveSupport::TestCase assert lock.locked? end - def it_unlocks_upon_calling_close_on_body + def test_it_unlocks_upon_calling_close_on_body lock = MyLock.new Dispatcher.expects(:reload_application) reloader = Reloader.new(lambda { |env| @@ -77,6 +77,18 @@ class ReloaderTests < ActiveSupport::TestCase assert !lock.locked? end + def test_it_unlocks_if_app_object_raises_exception + lock = MyLock.new + Dispatcher.expects(:reload_application) + reloader = Reloader.new(lambda { |env| + raise "oh no!" + }, lock) + assert_raise(RuntimeError) do + reloader.call({ }) + end + assert !lock.locked? + end + def test_returned_body_object_always_responds_to_close body = setup_and_return_body(lambda { |env| [200, { "Content-Type" => "text/html" }, [""]] -- 1.6.4.2 From e82b43599e53b8d1998f8b385c061153719781c0 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 10 Aug 2009 18:34:24 -0400 Subject: [PATCH 242/779] Allow delegating to nil, because the method might actually exist on it --- .../active_support/core_ext/module/delegation.rb | 13 +++++++++---- activesupport/test/core_ext/module_test.rb | 13 ++++++++++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index 9377bff..d7796fc 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -120,10 +120,15 @@ class Module end module_eval(<<-EOS, file, line) - def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block) - #{on_nil} if #{to}.nil? - #{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block) - end # end + def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block) + #{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block) + rescue NoMethodError # rescue NoMethodError + if #{to}.nil? # if client.nil? + #{on_nil} + else # else + raise # raise + end # end + end # end EOS end end diff --git a/activesupport/test/core_ext/module_test.rb b/activesupport/test/core_ext/module_test.rb index 7fb6e14..b897497 100644 --- a/activesupport/test/core_ext/module_test.rb +++ b/activesupport/test/core_ext/module_test.rb @@ -31,7 +31,7 @@ end Somewhere = Struct.new(:street, :city) Someone = Struct.new(:name, :place) do - delegate :street, :city, :to => :place + delegate :street, :city, :to_f, :to => :place delegate :state, :to => :@place delegate :upcase, :to => "place.city" end @@ -43,6 +43,7 @@ end Project = Struct.new(:description, :person) do delegate :name, :to => :person, :allow_nil => true + delegate :to_f, :to => :description, :allow_nil => true end class Name @@ -144,6 +145,16 @@ class ModuleTest < Test::Unit::TestCase assert_raise(RuntimeError) { david.street } end + def test_delegation_to_method_that_exists_on_nil + nil_person = Someone.new(nil) + assert_equal 0.0, nil_person.to_f + end + + def test_delegation_to_method_that_exists_on_nil_when_allowing_nil + nil_project = Project.new(nil) + assert_equal 0.0, nil_project.to_f + end + def test_parent assert_equal Yz::Zy, Yz::Zy::Cd.parent assert_equal Yz, Yz::Zy.parent -- 1.6.4.2 From 6f5d1f31907be5ff514c61bed9dc776eb3e5b76a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 11 Aug 2009 01:25:15 -0500 Subject: [PATCH 243/779] 'would of' => 'will be' --- .../lib/action_controller/routing/route_set.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 41236c1..671cbad 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -417,7 +417,7 @@ module ActionController new_results = route2.__send__(method, options, merged, expire_on) if new_results && (!new_results.is_a?(Array) || new_results.first) ActiveSupport::Deprecation.warn "The URL you generated will use the first matching route in routes.rb rather than the \"best\" match. " + - "In Rails 3.0 #{new_results} would of been generated instead of #{results}" + "In Rails 3.0 #{new_results} will be generated instead of #{results}" break end end -- 1.6.4.2 From dad0f62dc9b8225872dbc2e4a7fb5aed5bffb66a Mon Sep 17 00:00:00 2001 From: Jay Pignata Date: Sat, 15 Aug 2009 00:05:26 -0400 Subject: [PATCH 244/779] Fix test_has_many_through_polymorphic_has_one on sqlite2 [#3054 state:resolved] Signed-off-by: Pratik Naik --- .../test/cases/associations/join_model_test.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 0a8aa18..21412d1 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -377,7 +377,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_has_many_through_polymorphic_has_one - assert_equal Tagging.find(1,2), authors(:david).tagging + assert_equal Tagging.find(1,2).sort_by { |t| t.id }, authors(:david).tagging end def test_has_many_through_polymorphic_has_many -- 1.6.4.2 From a249cad5eff862cb3ceb26d1499ccb8b00d1ceb3 Mon Sep 17 00:00:00 2001 From: Jay Pignata Date: Fri, 14 Aug 2009 23:54:37 -0400 Subject: [PATCH 245/779] Fix calculation tests on sqlite2 [#3053 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/calculations.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 6eee401..eb149e8 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -190,7 +190,7 @@ module ActiveRecord sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group] if options[:from] sql << " FROM #{options[:from]} " - elsif scope && scope[:from] + elsif scope && scope[:from] && !use_workaround sql << " FROM #{scope[:from]} " else sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround -- 1.6.4.2 From dbc62ad225660453a6a955e507401504dffb8e20 Mon Sep 17 00:00:00 2001 From: Jatinder Singh Date: Fri, 14 Aug 2009 05:00:22 -0700 Subject: [PATCH 246/779] Fix ActiveResource load test for 64bit machines [#3051 state:resolved] Signed-off-by: Pratik Naik --- activeresource/test/base/load_test.rb | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activeresource/test/base/load_test.rb b/activeresource/test/base/load_test.rb index e4121d3..079ec2c 100644 --- a/activeresource/test/base/load_test.rb +++ b/activeresource/test/base/load_test.rb @@ -50,8 +50,8 @@ class BaseLoadTest < Test::Unit::TestCase :notable_rivers => [ { :id => 1, :name => 'Willamette' }, { :id => 2, :name => 'Columbia', :rafted_by => @matz }], - :postal_codes => [97018,1234567890], - :places => ["Columbia City", "Unknown"]}}} + :postal_codes => [ 97018, 1234567890 ], + :places => [ "Columbia City", "Unknown" ]}}} @person = Person.new end @@ -133,7 +133,7 @@ class BaseLoadTest < Test::Unit::TestCase assert_equal 2, postal_codes.size assert_kind_of Fixnum, postal_codes.first assert_equal @deep[:street][:state][:postal_codes].first, postal_codes.first - assert_kind_of Bignum, postal_codes.last + assert_kind_of Numeric, postal_codes.last assert_equal @deep[:street][:state][:postal_codes].last, postal_codes.last places = state.places -- 1.6.4.2 From 061b0ba6cbe6874b3c8b7edb372707411e719da6 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 15 Aug 2009 15:53:20 -0700 Subject: [PATCH 247/779] Refine the deprecated route check to explicitly check whether the future route pick comes before the deprecated route that was found. --- .../lib/action_controller/routing/route_set.rb | 11 ++++++----- 1 files changed, 6 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 671cbad..329ba5b 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -405,17 +405,18 @@ module ActionController end # don't use the recalled keys when determining which routes to check - routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }] + future_routes, deprecated_routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }] + no_worries = future_routes == deprecated_routes - routes[1].each_with_index do |route, index| + deprecated_routes.each_with_index do |route, index| results = route.__send__(method, options, merged, expire_on) if results && (!results.is_a?(Array) || results.first) # Compare results with Rails 3.0 behavior - if routes[0][index] != route - routes[0].each do |route2| + unless no_worries + future_routes.each_with_index do |route2, index2| new_results = route2.__send__(method, options, merged, expire_on) - if new_results && (!new_results.is_a?(Array) || new_results.first) + if new_results && (!new_results.is_a?(Array) || new_results.first) && index2 < future_routes.index(route) ActiveSupport::Deprecation.warn "The URL you generated will use the first matching route in routes.rb rather than the \"best\" match. " + "In Rails 3.0 #{new_results} will be generated instead of #{results}" break -- 1.6.4.2 From 75a483e18e3c4d5d7e6d4030a35db1edfcf7ae28 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 15 Aug 2009 15:58:20 -0700 Subject: [PATCH 248/779] Normalize route generation order: associations, yield block, then own routes. --- actionpack/lib/action_controller/resources.rb | 18 +++++++++--------- 1 files changed, 9 insertions(+), 9 deletions(-) diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index 919fb75..8c2080e 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -528,14 +528,14 @@ module ActionController with_options :controller => resource.controller do |map| map_associations(resource, options) + if block_given? + with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block) + end + map_collection_actions(map, resource) map_default_collection_actions(map, resource) map_new_actions(map, resource) map_member_actions(map, resource) - - if block_given? - with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block) - end end end @@ -543,16 +543,16 @@ module ActionController resource = SingletonResource.new(entities, options) with_options :controller => resource.controller do |map| - map_collection_actions(map, resource) - map_new_actions(map, resource) - map_member_actions(map, resource) - map_default_singleton_actions(map, resource) - map_associations(resource, options) if block_given? with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block) end + + map_collection_actions(map, resource) + map_new_actions(map, resource) + map_member_actions(map, resource) + map_default_singleton_actions(map, resource) end end -- 1.6.4.2 From 1cf32ad35a32cc1259a5aebfb894f53610ffac8e Mon Sep 17 00:00:00 2001 From: Jay Pignata Date: Thu, 13 Aug 2009 09:41:00 -0400 Subject: [PATCH 249/779] Adding a call to logger from params_parser to give detailed debug information when invalid xml or json is posted [#2481 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/params_parser.rb | 6 ++++ .../controller/request/json_params_parsing_test.rb | 28 +++++++++++++++++--- .../controller/request/xml_params_parsing_test.rb | 15 ++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_controller/params_parser.rb b/actionpack/lib/action_controller/params_parser.rb index d269fe0..f7e3c54 100644 --- a/actionpack/lib/action_controller/params_parser.rb +++ b/actionpack/lib/action_controller/params_parser.rb @@ -47,6 +47,8 @@ module ActionController false end rescue Exception => e # YAML, XML or Ruby code block errors + logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}" + raise { "body" => request.raw_post, "content_type" => request.content_type, @@ -67,5 +69,9 @@ module ActionController nil end + + def logger + defined?(Rails.logger) ? Rails.logger : Logger.new($stderr) + end end end diff --git a/actionpack/test/controller/request/json_params_parsing_test.rb b/actionpack/test/controller/request/json_params_parsing_test.rb index a3dde72..db6cf7b 100644 --- a/actionpack/test/controller/request/json_params_parsing_test.rb +++ b/actionpack/test/controller/request/json_params_parsing_test.rb @@ -30,16 +30,36 @@ class JsonParamsParsingTest < ActionController::IntegrationTest ) end + test "logs error if parsing unsuccessful" do + with_test_routing do + begin + $stderr = StringIO.new + json = "[\"person]\": {\"name\": \"David\"}}" + post "/parse", json, {'CONTENT_TYPE' => 'application/json'} + assert_response :error + $stderr.rewind && err = $stderr.read + assert err =~ /Error occurred while parsing request parameters/ + ensure + $stderr = STDERR + end + end + end + private def assert_parses(expected, actual, headers = {}) + with_test_routing do + post "/parse", actual, headers + assert_response :ok + assert_equal(expected, TestController.last_request_parameters) + end + end + + def with_test_routing with_routing do |set| set.draw do |map| map.connect ':action', :controller => "json_params_parsing_test/test" end - - post "/parse", actual, headers - assert_response :ok - assert_equal(expected, TestController.last_request_parameters) + yield end end end diff --git a/actionpack/test/controller/request/xml_params_parsing_test.rb b/actionpack/test/controller/request/xml_params_parsing_test.rb index ee764e7..170cf77 100644 --- a/actionpack/test/controller/request/xml_params_parsing_test.rb +++ b/actionpack/test/controller/request/xml_params_parsing_test.rb @@ -38,6 +38,21 @@ class XmlParamsParsingTest < ActionController::IntegrationTest end end + test "logs error if parsing unsuccessful" do + with_test_routing do + begin + $stderr = StringIO.new + xml = "David#{ActiveSupport::Base64.encode64('ABC')}" + post "/parse", xml, default_headers + assert_response :error + $stderr.rewind && err = $stderr.read + assert err =~ /Error occurred while parsing request parameters/ + ensure + $stderr = STDERR + end + end + end + test "parses multiple files" do xml = <<-end_body -- 1.6.4.2 From 14b6ab0f013f01541fca9fd42b33da96313e0a2e Mon Sep 17 00:00:00 2001 From: Hongli Lai (Phusion) Date: Tue, 11 Aug 2009 13:39:48 +0200 Subject: [PATCH 250/779] Fix reloading of metal pieces. - Do not hold references to old metal objects after metal classes have been reloaded. - Obtain the reloader lock before building the middleware stack, so that reloading of metal pieces works in the face of multithreading. [#2873 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/dispatcher.rb | 27 ++++++-- actionpack/lib/action_controller/reloader.rb | 20 +++---- actionpack/test/abstract_unit.rb | 16 +++++ actionpack/test/controller/dispatcher_test.rb | 61 +++++++++++++++--- actionpack/test/controller/reloader_test.rb | 79 ++++++++---------------- 5 files changed, 122 insertions(+), 81 deletions(-) diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 07931e4..1932944 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -2,13 +2,12 @@ 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 + @@cache_classes = true + class << self def define_dispatcher_callbacks(cache_classes) + @@cache_classes = cache_classes unless cache_classes - unless self.middleware.include?(Reloader) - self.middleware.insert_after(Failsafe, Reloader) - end - ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false end @@ -79,7 +78,7 @@ module ActionController # DEPRECATE: Remove arguments, since they are only used by CGI def initialize(output = $stdout, request = nil, response = nil) @output = output - @app = @@middleware.build(lambda { |env| self.dup._call(env) }) + build_middleware_stack if @@cache_classes end def dispatch @@ -103,7 +102,18 @@ module ActionController end def call(env) - @app.call(env) + if @@cache_classes + @app.call(env) + else + Reloader.run do + # When class reloading is turned on, we will want to rebuild the + # middleware stack every time we process a request. If we don't + # rebuild the middleware stack, then the stack may contain references + # to old classes metal classes, which will b0rk class reloading. + build_middleware_stack + @app.call(env) + end + end end def _call(env) @@ -114,5 +124,10 @@ module ActionController def flush_logger Base.logger.flush end + + private + def build_middleware_stack + @app = @@middleware.build(lambda { |env| self.dup._call(env) }) + end end end diff --git a/actionpack/lib/action_controller/reloader.rb b/actionpack/lib/action_controller/reloader.rb index e47d5ce..709d88a 100644 --- a/actionpack/lib/action_controller/reloader.rb +++ b/actionpack/lib/action_controller/reloader.rb @@ -2,7 +2,8 @@ require 'thread' module ActionController class Reloader - @@lock = Mutex.new + @@default_lock = Mutex.new + cattr_accessor :default_lock class BodyWrapper def initialize(body, lock) @@ -26,16 +27,11 @@ module ActionController end end - def initialize(app, lock = @@lock) - @app = app - @lock = lock - end - - def call(env) - @lock.lock - Dispatcher.reload_application + def self.run(lock = @@default_lock) + lock.lock begin - status, headers, body = @app.call(env) + Dispatcher.reload_application + status, headers, body = yield # We do not want to call 'cleanup_application' in an ensure block # because the returned Rack response body may lazily generate its data. This # is for example the case if one calls @@ -48,9 +44,9 @@ module ActionController # completely finished. So we wrap the body in a BodyWrapper class so that # when the Rack handler calls #close during the end of the request, we get to # run our cleanup code. - [status, headers, BodyWrapper.new(body, @lock)] + [status, headers, BodyWrapper.new(body, lock)] rescue Exception - @lock.unlock + lock.unlock raise end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 0b67e56..8acc451 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -43,3 +43,19 @@ ActionController::Base.view_paths = FIXTURE_LOAD_PATH CACHED_VIEW_PATHS = ActionView::Base.cache_template_loading? ? ActionController::Base.view_paths : ActionController::Base.view_paths.map {|path| ActionView::Template::EagerPath.new(path.to_s)} + +class DummyMutex + def lock + @locked = true + end + + def unlock + @locked = false + end + + def locked? + @locked + end +end + +ActionController::Reloader.default_lock = DummyMutex.new \ No newline at end of file diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index 3aa5bf9..61e7007 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -2,25 +2,17 @@ require 'abstract_unit' class DispatcherTest < Test::Unit::TestCase Dispatcher = ActionController::Dispatcher + Reloader = ActionController::Reloader def setup ENV['REQUEST_METHOD'] = 'GET' - - Dispatcher.middleware = ActionController::MiddlewareStack.new do |middleware| - middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/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) - + reset_dispatcher Dispatcher.stubs(:require_dependency) end def teardown ENV.delete 'REQUEST_METHOD' + reset_dispatcher end def test_clears_dependencies_after_dispatch_if_in_loading_mode @@ -41,6 +33,34 @@ class DispatcherTest < Test::Unit::TestCase dispatch end + def test_builds_middleware_stack_only_during_initialization_if_not_in_loading_mode + dispatcher = create_dispatcher + assert_not_nil dispatcher.instance_variable_get(:"@app") + dispatcher.instance_variable_set(:"@app", lambda { |env| }) + dispatcher.expects(:build_middleware_stack).never + dispatcher.call(nil) + dispatcher.call(nil) + end + + def test_rebuilds_middleware_stack_on_every_request_if_in_loading_mode + dispatcher = create_dispatcher(false) + dispatcher.instance_variable_set(:"@app", lambda { |env| }) + dispatcher.expects(:build_middleware_stack).twice + dispatcher.call(nil) + Reloader.default_lock.unlock + dispatcher.call(nil) + end + + def test_doesnt_wrap_call_in_reloader_if_not_in_loading_mode + Reloader.expects(:run).never + dispatch + end + + def test_wraps_call_in_reloader_if_in_loading_mode + Reloader.expects(:run).once + dispatch(false) + end + # Stub out dispatch error logger class << Dispatcher def log_failsafe_exception(status, exception); end @@ -99,6 +119,25 @@ class DispatcherTest < Test::Unit::TestCase Dispatcher.new.call({'rack.input' => StringIO.new('')}) end + def create_dispatcher(cache_classes = true) + Dispatcher.define_dispatcher_callbacks(cache_classes) + Dispatcher.new + end + + def reset_dispatcher + Dispatcher.middleware = ActionController::MiddlewareStack.new do |middleware| + middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/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) + + Dispatcher.define_dispatcher_callbacks(true) + end + def assert_subclasses(howmany, klass, message = klass.subclasses.inspect) assert_equal howmany, klass.subclasses.size, message end diff --git a/actionpack/test/controller/reloader_test.rb b/actionpack/test/controller/reloader_test.rb index 9eab9e1..e530549 100644 --- a/actionpack/test/controller/reloader_test.rb +++ b/actionpack/test/controller/reloader_test.rb @@ -22,87 +22,62 @@ class ReloaderTests < ActiveSupport::TestCase end end - class MyLock - def lock - @locked = true - end - - def unlock - @locked = false - end - - def locked? - @locked - end - end - def setup @lock = Mutex.new end - def setup_and_return_body(app = lambda { |env| }) + def test_it_reloads_the_application_before_yielding Dispatcher.expects(:reload_application) - reloader = Reloader.new(app, @lock) - headers, status, body = reloader.call({ }) - body - end - - def test_it_reloads_the_application_before_the_request - Dispatcher.expects(:reload_application) - reloader = Reloader.new(lambda { |env| + Reloader.run(@lock) do [200, { "Content-Type" => "text/html" }, [""]] - }, @lock) - reloader.call({ }) + end end - def test_it_locks_before_calling_app - lock = MyLock.new + def test_it_locks_before_yielding + lock = DummyMutex.new Dispatcher.expects(:reload_application) - reloader = Reloader.new(lambda { |env| + Reloader.run(lock) do + assert lock.locked? [200, { "Content-Type" => "text/html" }, [""]] - }, lock) - assert !lock.locked? - reloader.call({ }) + end assert lock.locked? end def test_it_unlocks_upon_calling_close_on_body - lock = MyLock.new + lock = DummyMutex.new Dispatcher.expects(:reload_application) - reloader = Reloader.new(lambda { |env| + headers, status, body = Reloader.run(lock) do [200, { "Content-Type" => "text/html" }, [""]] - }, lock) - headers, status, body = reloader.call({ }) + end body.close assert !lock.locked? end def test_it_unlocks_if_app_object_raises_exception - lock = MyLock.new + lock = DummyMutex.new Dispatcher.expects(:reload_application) - reloader = Reloader.new(lambda { |env| - raise "oh no!" - }, lock) assert_raise(RuntimeError) do - reloader.call({ }) + Reloader.run(lock) do + raise "oh no!" + end end assert !lock.locked? end def test_returned_body_object_always_responds_to_close - body = setup_and_return_body(lambda { |env| + status, headers, body = Reloader.run(@lock) do [200, { "Content-Type" => "text/html" }, [""]] - }) + end assert body.respond_to?(:close) end def test_returned_body_object_behaves_like_underlying_object - body = setup_and_return_body(lambda { |env| + status, headers, body = Reloader.run(@lock) do b = MyBody.new b << "hello" b << "world" [200, { "Content-Type" => "text/html" }, b] - }) + end assert_equal 2, body.size assert_equal "hello", body[0] assert_equal "world", body[1] @@ -112,20 +87,20 @@ class ReloaderTests < ActiveSupport::TestCase def test_it_calls_close_on_underlying_object_when_close_is_called_on_body close_called = false - body = setup_and_return_body(lambda { |env| + status, headers, body = Reloader.run(@lock) do b = MyBody.new do close_called = true end [200, { "Content-Type" => "text/html" }, b] - }) + end body.close assert close_called end def test_returned_body_object_responds_to_all_methods_supported_by_underlying_object - body = setup_and_return_body(lambda { |env| + status, headers, body = Reloader.run(@lock) do [200, { "Content-Type" => "text/html" }, MyBody.new] - }) + end assert body.respond_to?(:size) assert body.respond_to?(:each) assert body.respond_to?(:foo) @@ -134,16 +109,16 @@ class ReloaderTests < ActiveSupport::TestCase def test_it_doesnt_clean_up_the_application_after_call Dispatcher.expects(:cleanup_application).never - body = setup_and_return_body(lambda { |env| + status, headers, body = Reloader.run(@lock) do [200, { "Content-Type" => "text/html" }, MyBody.new] - }) + end end def test_it_cleans_up_the_application_when_close_is_called_on_body Dispatcher.expects(:cleanup_application) - body = setup_and_return_body(lambda { |env| + status, headers, body = Reloader.run(@lock) do [200, { "Content-Type" => "text/html" }, MyBody.new] - }) + end body.close end end -- 1.6.4.2 From 1cb433ce78cd18c433592f27f35172360a98ef19 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 15 Aug 2009 19:04:16 -0700 Subject: [PATCH 251/779] Bump pg gem requirement to 0.8.0. Build psql db with UTF8 encoding. --- activerecord/Rakefile | 4 ++-- ci/geminstaller.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 632ea40..0d2d9f7 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -75,8 +75,8 @@ task :rebuild_mysql_databases => 'mysql:rebuild_databases' namespace :postgresql do desc 'Build the PostgreSQL test databases' task :build_databases do - %x( createdb activerecord_unittest ) - %x( createdb activerecord_unittest2 ) + %x( createdb -E UTF8 activerecord_unittest ) + %x( createdb -E UTF8 activerecord_unittest2 ) end desc 'Drop the PostgreSQL test databases' diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml index a04bf94..39e3fc9 100644 --- a/ci/geminstaller.yml +++ b/ci/geminstaller.yml @@ -12,7 +12,7 @@ gems: #version: >= 2.7 version: = 2.7 - name: pg - version: >= 0.7.9.2008.10.13 + version: >= 0.8.0 - name: rack version: '~> 1.0.0' - name: rake -- 1.6.4.2 From d8ae3d5a8bdc9aca76bd3823d981473915404dab Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 15 Aug 2009 20:31:02 -0700 Subject: [PATCH 252/779] 2-3-stable CI notifies rails core list --- ci/cruise_config.rb | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/ci/cruise_config.rb b/ci/cruise_config.rb index 325c213..15a1bd6 100644 --- a/ci/cruise_config.rb +++ b/ci/cruise_config.rb @@ -1,6 +1,5 @@ Project.configure do |project| project.build_command = '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.emails = ['rubyonrails-core@googlegroups.com', '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' end -- 1.6.4.2 From b9f668ea944568639385bb99beac7db4a2286816 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 17 Aug 2009 14:49:31 +0100 Subject: [PATCH 253/779] Deprecate SQLite2Adapter and DeprecatedSQLiteAdapter --- .../connection_adapters/sqlite_adapter.rb | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 9f8075b..0bf97a9 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -16,6 +16,10 @@ module ActiveRecord db.results_as_hash = true if defined? SQLite::Version db.type_translation = false + message = "Support for SQLite2Adapter and DeprecatedSQLiteAdapter has been removed from Rails 3. " + message << "You should migrate to SQLite 3+ or use the plugin from git://github.com/rails/sqlite2_adapter.git with Rails 3." + ActiveSupport::Deprecation.warn(message) + # "Downgrade" deprecated sqlite API if SQLite.const_defined?(:Version) ConnectionAdapters::SQLite2Adapter.new(db, logger, config) -- 1.6.4.2 From f3c7bbeedd81d2f379c5e6a9e8739d3b3784ca5f Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 18 Aug 2009 16:08:05 +0100 Subject: [PATCH 254/779] Added db/seeds.rb as a default file for storing seed data for the database. Can be loaded with rake db:seed (or created alongside the db with db:setup). (This is also known as the "Stop Putting Gawd Damn Seed Data In Your Migrations" feature) [DHH] Conflicts: railties/CHANGELOG --- railties/Rakefile | 3 +++ railties/configs/seeds.rb | 7 +++++++ .../generators/applications/app/app_generator.rb | 5 +++++ railties/lib/tasks/databases.rake | 13 +++++++++++-- 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 railties/configs/seeds.rb diff --git a/railties/Rakefile b/railties/Rakefile index 9bdb337..5a3d65c 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -199,11 +199,14 @@ task :copy_configs do cp "configs/locales/en.yml", "#{PKG_DESTINATION}/config/locales/en.yml" + cp "configs/seeds.rb", "#{PKG_DESTINATION}/db/seeds.rb" + cp "environments/boot.rb", "#{PKG_DESTINATION}/config/boot.rb" cp "environments/environment.rb", "#{PKG_DESTINATION}/config/environment.rb" cp "environments/production.rb", "#{PKG_DESTINATION}/config/environments/production.rb" cp "environments/development.rb", "#{PKG_DESTINATION}/config/environments/development.rb" cp "environments/test.rb", "#{PKG_DESTINATION}/config/environments/test.rb" + end task :copy_binfiles do diff --git a/railties/configs/seeds.rb b/railties/configs/seeds.rb new file mode 100644 index 0000000..3174d0c --- /dev/null +++ b/railties/configs/seeds.rb @@ -0,0 +1,7 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). +# +# Examples: +# +# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }]) +# Major.create(:name => 'Daley', :city => cities.first) diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb index 2c31d89..c8c2239 100644 --- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb +++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb @@ -125,6 +125,7 @@ class AppGenerator < Rails::Generator::Base create_database_configuration_file(m) create_routes_file(m) create_locale_file(m) + create_seeds_file(m) create_initializer_files(m) create_environment_files(m) end @@ -176,6 +177,10 @@ class AppGenerator < Rails::Generator::Base m.file "configs/routes.rb", "config/routes.rb" end + def create_seeds_file(m) + m.file "configs/seeds.rb", "db/seeds.rb" + end + def create_initializer_files(m) %w( backtrace_silencers diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake index fbad8a1..b86b5c0 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/tasks/databases.rake @@ -156,8 +156,8 @@ namespace :db do Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby end - desc 'Drops and recreates the database from db/schema.rb for the current environment.' - task :reset => ['db:drop', 'db:create', 'db:schema:load'] + desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' + task :reset => [ 'db:drop', 'db:setup' ] desc "Retrieves the charset for the current environment's database" task :charset => :environment do @@ -206,6 +206,15 @@ namespace :db do end end + desc 'Create the database, load the schema, and initialize with the seed data' + task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ] + + desc 'Load the seed data from db/seeds.rb' + task :seed => :environment do + seed_file = File.join(Rails.root, 'db', 'seeds.rb') + load(seed_file) if File.exist?(seed_file) + end + namespace :fixtures do desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." task :load => :environment do -- 1.6.4.2 From 5f6e788e27d495af3fa76d0286af8814cd2652b9 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 18 Aug 2009 11:15:31 -0700 Subject: [PATCH 255/779] Removes examples so it can be replaced with separate repo --- actionpack/examples/minimal.rb | 87 ---------------------- actionpack/examples/views/_collection.erb | 1 - actionpack/examples/views/_hello.erb | 1 - actionpack/examples/views/_many_partials.erb | 10 --- actionpack/examples/views/_partial.erb | 10 --- actionpack/examples/views/layouts/alt.html.erb | 1 - actionpack/examples/views/layouts/kaigi.html.erb | 1 - actionpack/examples/views/template.html.erb | 1 - 8 files changed, 0 insertions(+), 112 deletions(-) delete mode 100644 actionpack/examples/minimal.rb delete mode 100644 actionpack/examples/views/_collection.erb delete mode 100644 actionpack/examples/views/_hello.erb delete mode 100644 actionpack/examples/views/_many_partials.erb delete mode 100644 actionpack/examples/views/_partial.erb delete mode 100644 actionpack/examples/views/layouts/alt.html.erb delete mode 100644 actionpack/examples/views/layouts/kaigi.html.erb delete mode 100644 actionpack/examples/views/template.html.erb diff --git a/actionpack/examples/minimal.rb b/actionpack/examples/minimal.rb deleted file mode 100644 index c777454..0000000 --- a/actionpack/examples/minimal.rb +++ /dev/null @@ -1,87 +0,0 @@ -$:.push File.join(File.dirname(__FILE__), "..", "lib") -$:.push File.join(File.dirname(__FILE__), "..", "..", "activesupport", "lib") -require "action_controller" - -class Runner - def initialize(app, output) - @app, @output = app, output - end - - def puts(*) - super if @output - end - - def call(env) - env['n'].to_i.times { @app.call(env) } - @app.call(env).tap { |response| report(env, response) } - end - - def report(env, response) - if ENV["DEBUG"] - out = env['rack.errors'] - p response.headers - out.puts response.status, response.headers.to_yaml, '---' - response.body.each { |part| out.puts part } - out.puts '---' - end - end - - def self.puts(*) - super if @output - end - - def self.run(app, n, label = nil, uri = "/", output = true) - @output = output - puts label, '=' * label.size if label - env = Rack::MockRequest.env_for(uri).merge('n' => n, 'rack.input' => StringIO.new(''), 'rack.errors' => $stdout) - t = Benchmark.realtime { new(app, output).call(env) } - puts "%d ms / %d req = %.1f usec/req" % [10**3 * t, n, 10**6 * t / n] - puts - end -end - -N = (ENV['N'] || 1000).to_i - -class BasePostController < ActionController::Base - append_view_path "#{File.dirname(__FILE__)}/views" - - def index - render :text => 'Hello' - end - - def partial - render :partial => "/partial" - end - - def many_partials - render :partial => "/many_partials" - end - - def partial_collection - render :partial => "/collection", :collection => [1,2,3,4,5,6,7,8,9,10] - end - - def show_template - render :template => "template" - end -end - -# p BasePostController.call(Rack::MockRequest.env_for("/?action=index").merge("REQUEST_URI" => "/")).body - -Runner.run(BasePostController, N, 'index', "/?action=index", false) -Runner.run(BasePostController, N, 'partial', "/?action=partial", false) -Runner.run(BasePostController, N, 'many partials', "/?action=many_partials", false) -Runner.run(BasePostController, N, 'collection', "/?action=partial_collection", false) -Runner.run(BasePostController, N, 'template', "/?action=show_template", false) - -(ENV["M"] || 1).to_i.times do - Runner.run(BasePostController, N, 'index', "/?action=index") - Runner.run(BasePostController, N, 'partial', "/?action=partial") - Runner.run(BasePostController, N, 'many partials', "/?action=many_partials") - Runner.run(BasePostController, N, 'collection', "/?action=partial_collection") - Runner.run(BasePostController, N, 'template', "/?action=show_template") -end - # Runner.run(BasePostController.action(:many_partials), N, 'index') - # Runner.run(BasePostController.action(:many_partials), N, 'many_partials') - # Runner.run(BasePostController.action(:partial_collection), N, 'collection') - # Runner.run(BasePostController.action(:show_template), N, 'template') diff --git a/actionpack/examples/views/_collection.erb b/actionpack/examples/views/_collection.erb deleted file mode 100644 index bcfe958..0000000 --- a/actionpack/examples/views/_collection.erb +++ /dev/null @@ -1 +0,0 @@ -<%= collection %> \ No newline at end of file diff --git a/actionpack/examples/views/_hello.erb b/actionpack/examples/views/_hello.erb deleted file mode 100644 index 5ab2f8a..0000000 --- a/actionpack/examples/views/_hello.erb +++ /dev/null @@ -1 +0,0 @@ -Hello \ No newline at end of file diff --git a/actionpack/examples/views/_many_partials.erb b/actionpack/examples/views/_many_partials.erb deleted file mode 100644 index 7e379d4..0000000 --- a/actionpack/examples/views/_many_partials.erb +++ /dev/null @@ -1,10 +0,0 @@ -<%= render :partial => '/hello' %> -<%= render :partial => '/hello' %> -<%= render :partial => '/hello' %> -<%= render :partial => '/hello' %> -<%= render :partial => '/hello' %> -<%= render :partial => '/hello' %> -<%= render :partial => '/hello' %> -<%= render :partial => '/hello' %> -<%= render :partial => '/hello' %> -<%= render :partial => '/hello' %> \ No newline at end of file diff --git a/actionpack/examples/views/_partial.erb b/actionpack/examples/views/_partial.erb deleted file mode 100644 index 3ca8e80..0000000 --- a/actionpack/examples/views/_partial.erb +++ /dev/null @@ -1,10 +0,0 @@ -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> -<%= "Hello" %> diff --git a/actionpack/examples/views/layouts/alt.html.erb b/actionpack/examples/views/layouts/alt.html.erb deleted file mode 100644 index c481633..0000000 --- a/actionpack/examples/views/layouts/alt.html.erb +++ /dev/null @@ -1 +0,0 @@ -+ <%= yield %> + \ No newline at end of file diff --git a/actionpack/examples/views/layouts/kaigi.html.erb b/actionpack/examples/views/layouts/kaigi.html.erb deleted file mode 100644 index 274607a..0000000 --- a/actionpack/examples/views/layouts/kaigi.html.erb +++ /dev/null @@ -1 +0,0 @@ -Hello <%= yield %> Goodbye \ No newline at end of file diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb deleted file mode 100644 index 5ab2f8a..0000000 --- a/actionpack/examples/views/template.html.erb +++ /dev/null @@ -1 +0,0 @@ -Hello \ No newline at end of file -- 1.6.4.2 From 9127c5b7f557d5206543d0256c265f5c85a196b5 Mon Sep 17 00:00:00 2001 From: Mike Gunderloy Date: Thu, 20 Aug 2009 06:28:22 -0500 Subject: [PATCH 256/779] Fix trivial typo in template runner example [#3082 state:resolved] Signed-off-by: Pratik Naik --- .../generators/applications/app/template_runner.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 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 3b49b1f..40f21cc 100644 --- a/railties/lib/rails_generator/generators/applications/app/template_runner.rb +++ b/railties/lib/rails_generator/generators/applications/app/template_runner.rb @@ -237,7 +237,7 @@ module Rails # ==== Example # # inside('vendor') do - # run('ln -s ~/edge rails) + # run('ln -s ~/edge rails') # end # def run(command, log_action = true) -- 1.6.4.2 From d6a944f778f015982994ef6156b77ef22f993733 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 23 Aug 2009 17:16:31 -0700 Subject: [PATCH 257/779] Add active_support/all for forward compatibility. --- activesupport/lib/active_support/all.rb | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) create mode 100644 activesupport/lib/active_support/all.rb diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb new file mode 100644 index 0000000..8a8805e --- /dev/null +++ b/activesupport/lib/active_support/all.rb @@ -0,0 +1,8 @@ +# For forward compatibility with Rails 3. +# +# require 'active_support' loads a very bare minumum in Rails 3. +# require 'active_support/all' loads the whole suite like Rails 2 did. +# +# To prepare for Rails 3, switch to require 'active_support/all' now. + +require 'active_support' -- 1.6.4.2 From f1355e6a4daf1e4c2bb65fb0149a2b3786f4c4d5 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 20 Aug 2009 21:09:59 -0700 Subject: [PATCH 258/779] Benchmark script via miloops' arel fork via DataMapper's AR comparison script --- activerecord/examples/.gitignore | 1 + activerecord/examples/performance.rb | 162 ++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 0 deletions(-) create mode 100644 activerecord/examples/.gitignore create mode 100755 activerecord/examples/performance.rb diff --git a/activerecord/examples/.gitignore b/activerecord/examples/.gitignore new file mode 100644 index 0000000..0dfc1cb --- /dev/null +++ b/activerecord/examples/.gitignore @@ -0,0 +1 @@ +performance.sql diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb new file mode 100755 index 0000000..12153b4 --- /dev/null +++ b/activerecord/examples/performance.rb @@ -0,0 +1,162 @@ +#!/usr/bin/env ruby -KU + +TIMES = (ENV['N'] || 10000).to_i + +require 'rubygems' +gem 'addressable', '~>2.0' +gem 'faker', '~>0.3.1' +gem 'rbench', '~>0.2.3' +require 'addressable/uri' +require 'faker' +require 'rbench' + +__DIR__ = File.dirname(__FILE__) +$:.unshift "#{__DIR__}/../lib" +require 'active_record' + +conn = { :adapter => 'mysql', + :database => 'activerecord_unittest', + :username => 'rails', :password => '', + :encoding => 'utf8' } + +conn[:socket] = Pathname.glob(%w[ + /opt/local/var/run/mysql5/mysqld.sock + /tmp/mysqld.sock + /tmp/mysql.sock + /var/mysql/mysql.sock + /var/run/mysqld/mysqld.sock +]).find { |path| path.socket? } + +ActiveRecord::Base.establish_connection(conn) + +class User < ActiveRecord::Base + connection.create_table :users, :force => true do |t| + t.string :name, :email + t.timestamps + end + + has_many :exhibits +end + +class Exhibit < ActiveRecord::Base + connection.create_table :exhibits, :force => true do |t| + t.belongs_to :user + t.string :name + t.text :notes + t.timestamps + end + + belongs_to :user + + def look; attributes end + def feel; look; user.name end + + def self.look(exhibits) exhibits.each { |e| e.look } end + def self.feel(exhibits) exhibits.each { |e| e.feel } end +end + +sqlfile = "#{__DIR__}/performance.sql" + +if File.exists?(sqlfile) + mysql_bin = %w[mysql mysql5].select { |bin| `which #{bin}`.length > 0 } + `#{mysql_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} < #{sqlfile}` +else + puts 'Generating data...' + + # pre-compute the insert statements and fake data compilation, + # so the benchmarks below show the actual runtime for the execute + # method, minus the setup steps + + # Using the same paragraph for all exhibits because it is very slow + # to generate unique paragraphs for all exhibits. + notes = Faker::Lorem.paragraphs.join($/) + today = Date.today + + puts 'Inserting 10,000 users and exhibits...' + 10_000.times do + user = User.create( + :created_on => today, + :name => Faker::Name.name, + :email => Faker::Internet.email + ) + + Exhibit.create( + :created_on => today, + :name => Faker::Company.name, + :user => user, + :notes => notes + ) + end + + mysqldump_bin = %w[mysqldump mysqldump5].select { |bin| `which #{bin}`.length > 0 } + `#{mysqldump_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} exhibits users > #{sqlfile}` +end + +RBench.run(TIMES) do + column :times + column :ar + + report 'Model#id', (TIMES * 100).ceil do + ar_obj = Exhibit.find(1) + + ar { ar_obj.id } + end + + report 'Model.new (instantiation)' do + ar { Exhibit.new } + end + + report 'Model.new (setting attributes)' do + attrs = { :name => 'sam' } + ar { Exhibit.new(attrs) } + end + + report 'Model.first' do + ar { Exhibit.first.look } + end + + report 'Model.all limit(100)', (TIMES / 10).ceil do + ar { Exhibit.look Exhibit.all(:limit => 100) } + end + + report 'Model.all limit(100) with relationship', (TIMES / 10).ceil do + ar { Exhibit.feel Exhibit.all(:limit => 100, :include => :user) } + end + + report 'Model.all limit(10,000)', (TIMES / 1000).ceil do + ar { Exhibit.look Exhibit.all(:limit => 10000) } + end + + exhibit = { + :name => Faker::Company.name, + :notes => Faker::Lorem.paragraphs.join($/), + :created_on => Date.today + } + + report 'Model.create' do + ar { Exhibit.create(exhibit) } + end + + report 'Resource#attributes=' do + attrs_first = { :name => 'sam' } + attrs_second = { :name => 'tom' } + ar { exhibit = Exhibit.new(attrs_first); exhibit.attributes = attrs_second } + end + + report 'Resource#update' do + ar { Exhibit.first.update_attributes(:name => 'bob') } + end + + report 'Resource#destroy' do + ar { Exhibit.first.destroy } + end + + report 'Model.transaction' do + ar { Exhibit.transaction { Exhibit.new } } + end + + summary 'Total' +end + +ActiveRecord::Migration.drop_table "exhibits" +ActiveRecord::Migration.drop_table "users" -- 1.6.4.2 From 38d6e65c5a059ffd07ed961ce79159a1801eba1c Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Mon, 24 Aug 2009 19:54:37 -0300 Subject: [PATCH 259/779] timestamps gives a created_at field not created_on. [#3093 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/examples/performance.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb index 12153b4..6841a04 100755 --- a/activerecord/examples/performance.rb +++ b/activerecord/examples/performance.rb @@ -75,13 +75,13 @@ else puts 'Inserting 10,000 users and exhibits...' 10_000.times do user = User.create( - :created_on => today, + :created_at => today, :name => Faker::Name.name, :email => Faker::Internet.email ) Exhibit.create( - :created_on => today, + :created_at => today, :name => Faker::Company.name, :user => user, :notes => notes -- 1.6.4.2 From e213f0caaac3d6e47be21f91aade5a0fd657cf5b Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 25 Aug 2009 13:41:37 -0700 Subject: [PATCH 260/779] Fix typo --- activerecord/examples/performance.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb index 6841a04..53acd62 100755 --- a/activerecord/examples/performance.rb +++ b/activerecord/examples/performance.rb @@ -130,7 +130,7 @@ RBench.run(TIMES) do exhibit = { :name => Faker::Company.name, :notes => Faker::Lorem.paragraphs.join($/), - :created_on => Date.today + :created_at => Date.today } report 'Model.create' do -- 1.6.4.2 From d725ad39da4436bded05cb647b930e5597c8771c Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 26 Aug 2009 14:00:06 +0200 Subject: [PATCH 261/779] I18n: use I18n for select helpers' prompt text [#2252 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/action_view/helpers/form_options_helper.rb | 3 +- actionpack/lib/action_view/locale/en.yml | 4 +++ .../test/template/form_options_helper_i18n_test.rb | 22 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletions(-) create mode 100644 actionpack/test/template/form_options_helper_i18n_test.rb diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 597e242..ec0e3d6 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -571,7 +571,8 @@ module ActionView option_tags = "\n" + option_tags end if value.blank? && options[:prompt] - ("\n") + option_tags + prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('support.select.prompt', :default => 'Please select') + "\n" + option_tags else option_tags end diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml index afe3569..c82cd07 100644 --- a/actionpack/lib/action_view/locale/en.yml +++ b/actionpack/lib/action_view/locale/en.yml @@ -108,3 +108,7 @@ # The variable :count is also available body: "There were problems with the following fields:" + support: + select: + # default value for :prompt => true in FormOptionsHelper + prompt: "Please select" \ No newline at end of file diff --git a/actionpack/test/template/form_options_helper_i18n_test.rb b/actionpack/test/template/form_options_helper_i18n_test.rb new file mode 100644 index 0000000..4f25d41 --- /dev/null +++ b/actionpack/test/template/form_options_helper_i18n_test.rb @@ -0,0 +1,22 @@ +require 'abstract_unit' + +class FormOptionsHelperI18nTests < ActionView::TestCase + tests ActionView::Helpers::FormOptionsHelper + + def setup + @prompt_message = 'Select!' + I18n.backend.store_translations :en, :support => { :select => { :prompt => @prompt_message} } + end + + def test_select_with_prompt_true_translates_prompt_message + I18n.expects(:translate).with('support.select.prompt', { :default => 'Please select' }) + select 'post', 'category', [], :prompt => true + end + + def test_select_with_translated_prompt + assert_dom_equal( + %Q(), + select('post', 'category', [], :prompt => true) + ) + end +end \ No newline at end of file -- 1.6.4.2 From a4838ee466b18ef04d0a244bec292c1d45c4bc0f Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 26 Aug 2009 19:22:56 +0200 Subject: [PATCH 262/779] allow ActiveRecord#RecordInvalid exception message to be localized [#2754 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/locale/en.yml | 1 + activerecord/lib/active_record/validations.rb | 3 ++- activerecord/test/cases/validations_i18n_test.rb | 10 ++++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml index bf8a71d..092f5f0 100644 --- a/activerecord/lib/active_record/locale/en.yml +++ b/activerecord/lib/active_record/locale/en.yml @@ -23,6 +23,7 @@ en: less_than_or_equal_to: "must be less than or equal to {{count}}" odd: "must be odd" even: "must be even" + record_invalid: "Validation failed: {{errors}}" # Append your own errors here or at the model/attributes scope. # You can define own errors for models or model attributes. diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 7a7d0ab..a23bb35 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -10,7 +10,8 @@ module ActiveRecord attr_reader :record def initialize(record) @record = record - super("Validation failed: #{@record.errors.full_messages.join(", ")}") + errors = @record.errors.full_messages.join(I18n.t('support.array.words_connector', :default => ', ')) + super(I18n.t('activerecord.errors.messages.record_invalid', :errors => errors)) end end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 1585554..5ddb0df 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -736,10 +736,9 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase @topic.valid? assert_equal "I am a custom error", @topic.errors.on(:title) end - end -class ActiveRecordValidationsGenerateMessageI18nTests < Test::Unit::TestCase +class ActiveRecordValidationsGenerateMessageI18nTests < ActiveSupport::TestCase def setup reset_callbacks Topic @topic = Topic.new @@ -917,5 +916,12 @@ class ActiveRecordValidationsGenerateMessageI18nTests < Test::Unit::TestCase def test_generate_message_even_with_default_message assert_equal "must be even", @topic.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10) end + # ActiveRecord#RecordInvalid exception + test "RecordInvalid exception can be localized" do + topic = Topic.new + topic.errors.add(:title, :invalid) + topic.errors.add(:title, :blank) + assert_equal "Validation failed: Title is invalid, Title can't be blank", ActiveRecord::RecordInvalid.new(topic).message + end end -- 1.6.4.2 From 3cd245b7fa8bf6afea1c41af503a6a1db28450a9 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 26 Aug 2009 12:12:04 -0700 Subject: [PATCH 263/779] Revert "I18n: use I18n for select helpers' prompt text" Broke CI. [#2252 state:open] This reverts commit d725ad39da4436bded05cb647b930e5597c8771c. --- .../lib/action_view/helpers/form_options_helper.rb | 3 +- actionpack/lib/action_view/locale/en.yml | 4 --- .../test/template/form_options_helper_i18n_test.rb | 22 -------------------- 3 files changed, 1 insertions(+), 28 deletions(-) delete mode 100644 actionpack/test/template/form_options_helper_i18n_test.rb diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index ec0e3d6..597e242 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -571,8 +571,7 @@ module ActionView option_tags = "\n" + option_tags end if value.blank? && options[:prompt] - prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('support.select.prompt', :default => 'Please select') - "\n" + option_tags + ("\n") + option_tags else option_tags end diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml index c82cd07..afe3569 100644 --- a/actionpack/lib/action_view/locale/en.yml +++ b/actionpack/lib/action_view/locale/en.yml @@ -108,7 +108,3 @@ # The variable :count is also available body: "There were problems with the following fields:" - support: - select: - # default value for :prompt => true in FormOptionsHelper - prompt: "Please select" \ No newline at end of file diff --git a/actionpack/test/template/form_options_helper_i18n_test.rb b/actionpack/test/template/form_options_helper_i18n_test.rb deleted file mode 100644 index 4f25d41..0000000 --- a/actionpack/test/template/form_options_helper_i18n_test.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'abstract_unit' - -class FormOptionsHelperI18nTests < ActionView::TestCase - tests ActionView::Helpers::FormOptionsHelper - - def setup - @prompt_message = 'Select!' - I18n.backend.store_translations :en, :support => { :select => { :prompt => @prompt_message} } - end - - def test_select_with_prompt_true_translates_prompt_message - I18n.expects(:translate).with('support.select.prompt', { :default => 'Please select' }) - select 'post', 'category', [], :prompt => true - end - - def test_select_with_translated_prompt - assert_dom_equal( - %Q(), - select('post', 'category', [], :prompt => true) - ) - end -end \ No newline at end of file -- 1.6.4.2 From e46e67c71f0afd2702b24896c94c56e3ba11a754 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 26 Aug 2009 14:00:06 +0200 Subject: [PATCH 264/779] I18n: use I18n for select helpers' prompt text [#2252 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/action_view/helpers/form_options_helper.rb | 3 +- actionpack/lib/action_view/locale/en.yml | 4 +++ .../test/template/form_options_helper_i18n_test.rb | 27 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletions(-) create mode 100644 actionpack/test/template/form_options_helper_i18n_test.rb diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 597e242..ec0e3d6 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -571,7 +571,8 @@ module ActionView option_tags = "\n" + option_tags end if value.blank? && options[:prompt] - ("\n") + option_tags + prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('support.select.prompt', :default => 'Please select') + "\n" + option_tags else option_tags end diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml index afe3569..c82cd07 100644 --- a/actionpack/lib/action_view/locale/en.yml +++ b/actionpack/lib/action_view/locale/en.yml @@ -108,3 +108,7 @@ # The variable :count is also available body: "There were problems with the following fields:" + support: + select: + # default value for :prompt => true in FormOptionsHelper + prompt: "Please select" \ No newline at end of file diff --git a/actionpack/test/template/form_options_helper_i18n_test.rb b/actionpack/test/template/form_options_helper_i18n_test.rb new file mode 100644 index 0000000..91e370e --- /dev/null +++ b/actionpack/test/template/form_options_helper_i18n_test.rb @@ -0,0 +1,27 @@ +require 'abstract_unit' + +class FormOptionsHelperI18nTests < ActionView::TestCase + tests ActionView::Helpers::FormOptionsHelper + + def setup + @prompt_message = 'Select!' + I18n.backend.send(:init_translations) + I18n.backend.store_translations :en, :support => { :select => { :prompt => @prompt_message } } + end + + def teardown + I18n.backend = I18n::Backend::Simple.new + end + + def test_select_with_prompt_true_translates_prompt_message + I18n.expects(:translate).with('support.select.prompt', { :default => 'Please select' }) + select('post', 'category', [], :prompt => true) + end + + def test_select_with_translated_prompt + assert_dom_equal( + %Q(), + select('post', 'category', [], :prompt => true) + ) + end +end \ No newline at end of file -- 1.6.4.2 From 4240890b28ea7d797d812202315ceeda55bac71e Mon Sep 17 00:00:00 2001 From: Jeffrey Hardy Date: Tue, 2 Jun 2009 13:28:44 -0400 Subject: [PATCH 265/779] UrlRewriter#rewrite_url should call #to_param on the value given in :anchor option, just as #url_for does [#2746 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/url_rewriter.rb | 2 +- actionpack/test/controller/url_rewriter_test.rb | 26 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb index bb6cb43..e4c2a29 100644 --- a/actionpack/lib/action_controller/url_rewriter.rb +++ b/actionpack/lib/action_controller/url_rewriter.rb @@ -184,7 +184,7 @@ module ActionController path = rewrite_path(options) rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root] rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path) - rewritten_url << "##{options[:anchor]}" if options[:anchor] + rewritten_url << "##{CGI.escape(options[:anchor].to_param.to_s)}" if options[:anchor] rewritten_url end diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb index 0e149cf..fdc4cfa 100644 --- a/actionpack/test/controller/url_rewriter_test.rb +++ b/actionpack/test/controller/url_rewriter_test.rb @@ -46,6 +46,20 @@ class UrlRewriterTests < ActionController::TestCase ) end + def test_anchor_should_call_to_param + assert_equal( + 'http://test.host/c/a/i#anchor', + @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anchor')) + ) + end + + def test_anchor_should_be_cgi_escaped + assert_equal( + 'http://test.host/c/a/i#anc%2Fhor', + @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => Struct.new(:to_param).new('anc/hor')) + ) + end + def test_overwrite_params @params[:controller] = 'hi' @params[:action] = 'bye' @@ -110,6 +124,18 @@ class UrlWriterTests < ActionController::TestCase ) end + def test_anchor_should_call_to_param + assert_equal('/c/a#anchor', + W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anchor')) + ) + end + + def test_anchor_should_be_cgi_escaped + assert_equal('/c/a#anc%2Fhor', + W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anc/hor')) + ) + end + def test_default_host add_host! assert_equal('http://www.basecamphq.com/c/a/i', -- 1.6.4.2 From 8a49183563d3f80ecd4024977429b62f671f9f7c Mon Sep 17 00:00:00 2001 From: Jeffrey Hardy Date: Wed, 26 Aug 2009 13:14:40 -0400 Subject: [PATCH 266/779] Don't use AR::Base.connection for fixture column quoting. Use the connection given to Fixtures.new [#3104 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/fixtures.rb | 16 +++++++++------- 1 files changed, 9 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 77f0931..09da78c 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -621,7 +621,8 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) targets.each do |target| join_fixtures["#{label}_#{target}"] = Fixture.new( { association.primary_key_name => row[primary_key_name], - association.association_foreign_key => Fixtures.identify(target) }, nil) + association.association_foreign_key => Fixtures.identify(target) }, + nil, @connection) end end end @@ -705,12 +706,12 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) yaml_value.each do |fixture| raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each) - fixture.each do |name, data| + fixture.each do |name, data| unless data raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)" end - self[name] = Fixture.new(data, model_class) + self[name] = Fixture.new(data, model_class, @connection) end end end @@ -723,7 +724,7 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) reader.each do |row| data = {} row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip } - self["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class) + self["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class, @connection) end end @@ -761,7 +762,8 @@ class Fixture #:nodoc: attr_reader :model_class - def initialize(fixture, model_class) + def initialize(fixture, model_class, connection = ActiveRecord::Base.connection) + @connection = connection @fixture = fixture @model_class = model_class.is_a?(Class) ? model_class : model_class.constantize rescue nil end @@ -783,14 +785,14 @@ class Fixture #:nodoc: end def key_list - columns = @fixture.keys.collect{ |column_name| ActiveRecord::Base.connection.quote_column_name(column_name) } + columns = @fixture.keys.collect{ |column_name| @connection.quote_column_name(column_name) } columns.join(", ") end def value_list list = @fixture.inject([]) do |fixtures, (key, value)| col = model_class.columns_hash[key] if model_class.respond_to?(:ancestors) && model_class.ancestors.include?(ActiveRecord::Base) - fixtures << ActiveRecord::Base.connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r") + fixtures << @connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r") end list * ', ' end -- 1.6.4.2 From 058459dc228722f1b49cc6644774872217185436 Mon Sep 17 00:00:00 2001 From: Jeffrey Hardy Date: Thu, 27 Aug 2009 13:58:43 -0400 Subject: [PATCH 267/779] When running multiple test tasks, don't abort early if one produces failures [#3107 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/Rakefile | 22 +++++++++++++++++++--- 1 files changed, 19 insertions(+), 3 deletions(-) diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 0d2d9f7..36e4303 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -24,14 +24,30 @@ PKG_FILES = FileList[ "lib/**/*", "test/**/*", "examples/**/*", "doc/**/*", "[A-Z]*", "install.rb", "Rakefile" ].exclude(/\bCVS\b|~$/) +def run_without_aborting(*tasks) + errors = [] + + tasks.each do |task| + begin + Rake::Task[task].invoke + rescue Exception + errors << task + end + end + + abort "Errors running #{errors.join(', ')}" if errors.any? +end desc 'Run mysql, sqlite, and postgresql tests by default' task :default => :test desc 'Run mysql, sqlite, and postgresql tests' -task :test => defined?(JRUBY_VERSION) ? - %w(test_jdbcmysql test_jdbcsqlite3 test_jdbcpostgresql) : - %w(test_mysql test_sqlite3 test_postgresql) +task :test do + tasks = defined?(JRUBY_VERSION) ? + %w(test_jdbcmysql test_jdbcsqlite3 test_jdbcpostgresql) : + %w(test_mysql test_sqlite3 test_postgresql) + run_without_aborting(*tasks) +end for adapter in %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase jdbcmysql jdbcpostgresql jdbcsqlite3 jdbcderby jdbch2 jdbchsqldb ) Rake::TestTask.new("test_#{adapter}") { |t| -- 1.6.4.2 From 05d7409ae5fd423be6f747ad553f659fcecbf548 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 27 Aug 2009 23:07:43 -0700 Subject: [PATCH 268/779] Prefer utf8_unicode_ci (better) over utf8_general_ci (faster) --- railties/lib/tasks/databases.rake | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake index b86b5c0..8b60839 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/tasks/databases.rake @@ -55,7 +55,7 @@ namespace :db do case config['adapter'] when 'mysql' @charset = ENV['CHARSET'] || 'utf8' - @collation = ENV['COLLATION'] || 'utf8_general_ci' + @collation = ENV['COLLATION'] || 'utf8_unicode_ci' begin ActiveRecord::Base.establish_connection(config.merge('database' => nil)) ActiveRecord::Base.connection.create_database(config['database'], :charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)) -- 1.6.4.2 From 13fb26b714dec0874303f51cc125ff62f65a2729 Mon Sep 17 00:00:00 2001 From: Sven Fuchs + Mateo Murphy Date: Sat, 22 Aug 2009 19:52:02 +0200 Subject: [PATCH 269/779] Fix ActiveRecord Error message I18n: * allow messages and full_messages to be lazily translated at any time * allow locales to be swapped and still obtain correctly localized messages * allow localized global and error-type specific full_message formats * extract an Error class [#1687 state:open] Signed-off-by: Jeremy Kemper --- .../lib/active_record/autosave_association.rb | 7 +- activerecord/lib/active_record/locale/en.yml | 3 + activerecord/lib/active_record/validations.rb | 222 +++-- activerecord/test/cases/validations_i18n_test.rb | 1280 ++++++++++---------- 4 files changed, 802 insertions(+), 710 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 4f63b52..1b73b0f 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -249,9 +249,10 @@ module ActiveRecord unless valid = association.valid? if reflection.options[:autosave] unless association.marked_for_destruction? - association.errors.each do |attribute, message| - attribute = "#{reflection.name}_#{attribute}" - errors.add(attribute, message) unless errors.on(attribute) + association.errors.each_error do |attribute, error| + error = error.dup + error.attribute = "#{reflection.name}_#{attribute}" + errors.add(error) unless errors.on(error.attribute) end end else diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml index 092f5f0..2813524 100644 --- a/activerecord/lib/active_record/locale/en.yml +++ b/activerecord/lib/active_record/locale/en.yml @@ -26,6 +26,9 @@ en: record_invalid: "Validation failed: {{errors}}" # Append your own errors here or at the model/attributes scope. + full_messages: + format: "{{attribute}} {{message}}" + # You can define own errors for models or model attributes. # The values :model, :attribute and :value are always available for interpolation. # diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index a23bb35..b339f0c 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -15,11 +15,117 @@ module ActiveRecord end end + class Error + attr_accessor :base, :attribute, :type, :message, :options + + def initialize(base, attribute, type = nil, options = {}) + self.base = base + self.attribute = attribute + self.type = type || :invalid + self.options = options + self.message = options.delete(:message) || self.type + end + + def message + generate_message(@message, options.dup) + end + + def full_message + attribute.to_s == 'base' ? message : generate_full_message(message, options.dup) + end + + alias :to_s :message + + def value + @base.respond_to?(attribute) ? @base.send(attribute) : nil + end + + protected + + # Translates an error message in it's default scope (activerecord.errrors.messages). + # Error messages are first looked up in models.MODEL.attributes.ATTRIBUTE.MESSAGE, if it's not there, + # it's looked up in models.MODEL.MESSAGE and if that is not there it returns the translation of the + # default message (e.g. activerecord.errors.messages.MESSAGE). The translated model name, + # translated attribute name and the value are available for interpolation. + # + # When using inheritence in your models, it will check all the inherited models too, but only if the model itself + # hasn't been found. Say you have class Admin < User; end and you wanted the translation for the :blank + # error +message+ for the title +attribute+, it looks for these translations: + # + #
    + #
  1. activerecord.errors.models.admin.attributes.title.blank
  2. + #
  3. activerecord.errors.models.admin.blank
  4. + #
  5. activerecord.errors.models.user.attributes.title.blank
  6. + #
  7. activerecord.errors.models.user.blank
  8. + #
  9. activerecord.errors.messages.blank
  10. + #
  11. any default you provided through the +options+ hash (in the activerecord.errors scope)
  12. + #
+ def generate_message(message, options = {}) + keys = @base.class.self_and_descendants_from_active_record.map do |klass| + [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}", + :"models.#{klass.name.underscore}.#{message}" ] + end.flatten + + keys << options.delete(:default) + keys << :"messages.#{message}" + keys << message if message.is_a?(String) + keys << @type unless @type == message + keys.compact! + + options.reverse_merge! :default => keys, + :scope => [:activerecord, :errors], + :model => @base.class.human_name, + :attribute => @base.class.human_attribute_name(attribute.to_s), + :value => value + + I18n.translate(keys.shift, options) + end + + # Wraps an error message into a full_message format. + # + # The default full_message format for any locale is "{{attribute}} {{message}}". + # One can specify locale specific default full_message format by storing it as a + # translation for the key :"activerecord.errors.full_messages.format". + # + # Additionally one can specify a validation specific error message format by + # storing a translation for :"activerecord.errors.full_messages.[message_key]". + # E.g. the full_message format for any validation that uses :blank as a message + # key (such as validates_presence_of) can be stored to :"activerecord.errors.full_messages.blank". + # + # Because the message key used by a validation can be overwritten on the + # validates_* class macro level one can customize the full_message format for + # any particular validation: + # + # # app/models/article.rb + # class Article < ActiveRecord::Base + # validates_presence_of :title, :message => :"title.blank" + # end + # + # # config/locales/en.yml + # en: + # activerecord: + # errors: + # full_messages: + # title: + # blank: This title is screwed! + def generate_full_message(message, options = {}) + options.reverse_merge! :message => self.message, + :model => @base.class.human_name, + :attribute => @base.class.human_attribute_name(attribute.to_s), + :value => value + + key = :"full_messages.#{@message}" + defaults = [:'full_messages.format', '{{attribute}} {{message}}'] + + I18n.t(key, options.merge(:default => defaults, :scope => [:activerecord, :errors])) + end + end + # Active Record validation is reported to and from this object, which is used by Base#save to # determine whether the object is in a valid state to be saved. See usage example in Validations. class Errors include Enumerable - + class << self def default_error_messages ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use I18n.translate('activerecord.errors.messages').") @@ -44,11 +150,19 @@ module ActiveRecord # error can be added to the same +attribute+ in which case an array will be returned on a call to on(attribute). # If no +messsage+ is supplied, :invalid is assumed. # If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error). - def add(attribute, message = nil, options = {}) - message ||= :invalid - message = generate_message(attribute, message, options) if message.is_a?(Symbol) + # def add(attribute, message = nil, options = {}) + # message ||= :invalid + # message = generate_message(attribute, message, options)) if message.is_a?(Symbol) + # @errors[attribute.to_s] ||= [] + # @errors[attribute.to_s] << message + # end + + def add(error_or_attr, message = nil, options = {}) + error, attribute = error_or_attr.is_a?(Error) ? [error_or_attr, error_or_attr.attribute] : [nil, error_or_attr] + options[:message] = options.delete(:default) if options.has_key?(:default) + @errors[attribute.to_s] ||= [] - @errors[attribute.to_s] << message + @errors[attribute.to_s] << (error || Error.new(@base, attribute, message, options)) end # Will add an error message to each of the attributes in +attributes+ that is empty. @@ -67,49 +181,6 @@ module ActiveRecord add(attr, :blank, :default => custom_message) if value.blank? end end - - # Translates an error message in it's default scope (activerecord.errrors.messages). - # Error messages are first looked up in models.MODEL.attributes.ATTRIBUTE.MESSAGE, if it's not there, - # it's looked up in models.MODEL.MESSAGE and if that is not there it returns the translation of the - # default message (e.g. activerecord.errors.messages.MESSAGE). The translated model name, - # translated attribute name and the value are available for interpolation. - # - # When using inheritence in your models, it will check all the inherited models too, but only if the model itself - # hasn't been found. Say you have class Admin < User; end and you wanted the translation for the :blank - # error +message+ for the title +attribute+, it looks for these translations: - # - #
    - #
  1. activerecord.errors.models.admin.attributes.title.blank
  2. - #
  3. activerecord.errors.models.admin.blank
  4. - #
  5. activerecord.errors.models.user.attributes.title.blank
  6. - #
  7. activerecord.errors.models.user.blank
  8. - #
  9. activerecord.errors.messages.blank
  10. - #
  11. any default you provided through the +options+ hash (in the activerecord.errors scope)
  12. - #
- def generate_message(attribute, message = :invalid, options = {}) - - message, options[:default] = options[:default], message if options[:default].is_a?(Symbol) - - defaults = @base.class.self_and_descendants_from_active_record.map do |klass| - [ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}", - :"models.#{klass.name.underscore}.#{message}" ] - end - - defaults << options.delete(:default) - defaults = defaults.compact.flatten << :"messages.#{message}" - - key = defaults.shift - value = @base.respond_to?(attribute) ? @base.send(attribute) : nil - - options = { :default => defaults, - :model => @base.class.human_name, - :attribute => @base.class.human_attribute_name(attribute.to_s), - :value => value, - :scope => [:activerecord, :errors] - }.merge(options) - - I18n.translate(key, options) - end # Returns true if the specified +attribute+ has errors associated with it. # @@ -139,8 +210,9 @@ module ActiveRecord # company.errors.on(:email) # => "can't be blank" # company.errors.on(:address) # => nil def on(attribute) - errors = @errors[attribute.to_s] - return nil if errors.nil? + attribute = attribute.to_s + return nil unless @errors.has_key?(attribute) + errors = @errors[attribute].map(&:to_s) errors.size == 1 ? errors.first : errors end @@ -164,7 +236,11 @@ module ActiveRecord # # name - can't be blank # # address - can't be blank def each - @errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } } + @errors.each_key { |attr| @errors[attr].each { |error| yield attr, error.message } } + end + + def each_error + @errors.each_key { |attr| @errors[attr].each { |error| yield attr, error } } end # Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned @@ -195,22 +271,10 @@ module ActiveRecord # company.errors.full_messages # => # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"] def full_messages(options = {}) - full_messages = [] - - @errors.each_key do |attr| - @errors[attr].each do |message| - next unless message - - if attr == "base" - full_messages << message - else - attr_name = @base.class.human_attribute_name(attr) - full_messages << attr_name + I18n.t('activerecord.errors.format.separator', :default => ' ') + message - end - end + @errors.values.inject([]) do |full_messages, errors| + full_messages + errors.map { |error| error.full_message } end - full_messages - end + end # Returns true if no errors have been added. def empty? @@ -255,7 +319,11 @@ module ActiveRecord full_messages.each { |msg| e.error(msg) } end end - + + def generate_message(attribute, message = :invalid, options = {}) + ActiveSupport::Deprecation.warn("ActiveRecord::Errors#generate_message has been deprecated. Please use ActiveRecord::Error#generate_message.") + Error.new(@base, attribute, message, options).to_s + end end @@ -438,7 +506,7 @@ module ActiveRecord validates_each(attr_names, configuration) do |record, attr_name, value| unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation") - record.errors.add(attr_name, :confirmation, :default => configuration[:message]) + record.errors.add(attr_name, :confirmation, :default => configuration[:message]) end end end @@ -480,7 +548,7 @@ module ActiveRecord validates_each(attr_names,configuration) do |record, attr_name, value| unless value == configuration[:accept] - record.errors.add(attr_name, :accepted, :default => configuration[:message]) + record.errors.add(attr_name, :accepted, :default => configuration[:message]) end end end @@ -500,7 +568,7 @@ module ActiveRecord # # Configuration options: # * message - A custom error message (default is: "can't be blank"). - # * on - Specifies when this validation is active (default is :save, other options :create, + # * on - Specifies when this validation is active (default is :save, other options :create, # :update). # * if - Specifies a method, proc or string to call to determine if the validation should # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). @@ -600,7 +668,7 @@ module ActiveRecord validates_each(attrs, options) do |record, attr, value| value = options[:tokenizer].call(value) if value.kind_of?(String) unless !value.nil? and value.size.method(validity_checks[option])[option_value] - record.errors.add(attr, key, :default => custom_message, :count => option_value) + record.errors.add(attr, key, :default => custom_message, :count => option_value) end end end @@ -688,7 +756,7 @@ module ActiveRecord # ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the # rare case that a race condition occurs, the database will guarantee # the field's uniqueness. - # + # # When the database catches such a duplicate insertion, # ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid # exception. You can either choose to let this error propagate (which @@ -697,7 +765,7 @@ module ActiveRecord # that the title already exists, and asking him to re-enter the title). # This technique is also known as optimistic concurrency control: # http://en.wikipedia.org/wiki/Optimistic_concurrency_control - # + # # Active Record currently provides no way to distinguish unique # index constraint errors from other types of database errors, so you # will have to parse the (database-specific) exception message to detect @@ -795,7 +863,7 @@ module ActiveRecord validates_each(attr_names, configuration) do |record, attr_name, value| unless value.to_s =~ configuration[:with] - record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) + record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) end end end @@ -829,7 +897,7 @@ module ActiveRecord validates_each(attr_names, configuration) do |record, attr_name, value| unless enum.include?(value) - record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value) + record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value) end end end @@ -863,7 +931,7 @@ module ActiveRecord validates_each(attr_names, configuration) do |record, attr_name, value| if enum.include?(value) - record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value) + record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value) end end end @@ -971,7 +1039,7 @@ module ActiveRecord case option when :odd, :even unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[] - record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message]) + record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message]) end else record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option]) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]] diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 5ddb0df..36eadd5 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -1,32 +1,20 @@ require "cases/helper" require 'models/topic' require 'models/reply' +require 'models/person' -class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase - def setup - reset_callbacks Topic - @topic = Topic.new - @old_load_path, @old_backend = I18n.load_path, I18n.backend - I18n.load_path.clear - I18n.backend = I18n::Backend::Simple.new - I18n.backend.store_translations('en', :activerecord => {:errors => {:messages => {:custom => nil}}}) - end - - def teardown - reset_callbacks Topic - I18n.load_path.replace @old_load_path - I18n.backend = @old_backend - end - - def unique_topic - @unique ||= Topic.create :title => 'unique!' +module ActiveRecordValidationsI18nTestHelper + def store_translations(*args) + data = args.extract_options! + locale = args.shift || 'en' + I18n.backend.send(:init_translations) + I18n.backend.store_translations(locale, :activerecord => data) end - def replied_topic - @replied_topic ||= begin - topic = Topic.create(:title => "topic") - topic.replies << Reply.new - topic + def delete_translation(key) + I18n.backend.instance_eval do + keys = I18n.send(:normalize_translation_keys, 'en', key, nil) + keys.inject(translations) { |result, k| keys.last == k ? result.delete(k.to_sym) : result[k.to_sym] } end end @@ -37,887 +25,919 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) end end +end + +# DEPRECATIONS - def test_default_error_messages_is_deprecated +class ActiveRecordValidationsI18nDeprecationsTests < ActiveSupport::TestCase + test "default_error_messages is deprecated and can be removed in Rails 3 / ActiveModel" do assert_deprecated('ActiveRecord::Errors.default_error_messages') do ActiveRecord::Errors.default_error_messages end end - def test_percent_s_interpolation_syntax_in_error_messages_still_works + test "%s interpolation syntax in error messages still works" do ActiveSupport::Deprecation.silence do result = I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this' assert_equal result, "this interpolation syntax is deprecated" end end - def test_percent_s_interpolation_syntax_in_error_messages_is_deprecated + test "%s interpolation syntax in error messages is deprecated" do assert_deprecated('using %s in messages') do - I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this' + I18n.t :does_not_exist, :default => "%s interpolation syntax is deprected", :value => 'this' end end - def test_percent_d_interpolation_syntax_in_error_messages_still_works + test "%d interpolation syntax in error messages still works" do ActiveSupport::Deprecation.silence do result = I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprecated", :count => 2 assert_equal result, "2 interpolation syntaxes are deprecated" end end - def test_percent_d_interpolation_syntax_in_error_messages_is_deprecated + test "%d interpolation syntax in error messages is deprecated" do assert_deprecated('using %d in messages') do I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected", :count => 2 end end +end - def test_percent_s_interpolation_syntax_not_changed_when_no_values_were_passed - assert_not_deprecated do - I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected" - end - end - - # ActiveRecord::Errors - def test_errors_generate_message_translates_custom_model_attribute_key - - I18n.expects(:translate).with( - :topic, - { :count => 1, - :default => ['Topic'], - :scope => [:activerecord, :models] - } - ).returns('Topic') - - I18n.expects(:translate).with( - :"topic.title", - { :count => 1, - :default => ['Title'], - :scope => [:activerecord, :attributes] - } - ).returns('Title') - - I18n.expects(:translate).with( - :"models.topic.attributes.title.invalid", - :value => nil, - :scope => [:activerecord, :errors], - :default => [ - :"models.topic.invalid", - 'default from class def error 1', - :"messages.invalid"], - :attribute => "Title", - :model => "Topic" - ).returns('default from class def error 1') - - @topic.errors.generate_message :title, :invalid, :default => 'default from class def error 1' - end - - def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti - - I18n.expects(:translate).with( - :reply, - { :count => 1, - :default => [:topic, 'Reply'], - :scope => [:activerecord, :models] - } - ).returns('Reply') - - I18n.expects(:translate).with( - :"reply.title", - { :count => 1, - :default => [:'topic.title', 'Title'], - :scope => [:activerecord, :attributes] - } - ).returns('Title') - I18n.expects(:translate).with( - :"models.reply.attributes.title.invalid", - :value => nil, - :scope => [:activerecord, :errors], - :default => [ - :"models.reply.invalid", - :"models.topic.attributes.title.invalid", - :"models.topic.invalid", - 'default from class def', - :"messages.invalid"], - :model => 'Reply', - :attribute => 'Title' - ).returns("default from class def") +# ACTIVERECORD VALIDATIONS +# +# For each validation: +# +# * test expect that it adds an error with the appropriate arguments +# * test that it looks up the correct default message - Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' +class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase + include ActiveRecordValidationsI18nTestHelper + def setup + reset_callbacks(Topic) + @topic = Topic.new + @reply = Reply.new + @old_load_path, @old_backend = I18n.load_path, I18n.backend + I18n.load_path.clear + I18n.backend = I18n::Backend::Simple.new + I18n.backend.store_translations('en', :activerecord => {:errors => {:messages => {:custom => nil}}}) end - def test_errors_add_on_empty_generates_message - @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil}) - @topic.errors.add_on_empty :title + def teardown + reset_callbacks(Topic) + I18n.load_path.replace(@old_load_path) + I18n.backend = @old_backend end - def test_errors_add_on_empty_generates_message_with_custom_default_message - @topic.errors.expects(:generate_message).with(:title, :empty, {:default => 'custom'}) - @topic.errors.add_on_empty :title, 'custom' + def expect_error_added(model, attribute, type, options) + model.errors.expects(:add).with(attribute, type, options) + yield + model.valid? end - def test_errors_add_on_blank_generates_message - @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) - @topic.errors.add_on_blank :title + def assert_message_translations(model, attribute, type, &block) + assert_default_message_translation(model, attribute, type, &block) + reset_callbacks(model.class) + model.errors.clear + assert_custom_message_translation(model, attribute, type, &block) end - def test_errors_add_on_blank_generates_message_with_custom_default_message - @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) - @topic.errors.add_on_blank :title, 'custom' + def assert_custom_message_translation(model, attribute, type) + store_translations(:errors => { :models => { model.class.name.underscore => { :attributes => { attribute => { type => 'custom message' } } } } }) + yield + model.valid? + assert_equal 'custom message', model.errors.on(attribute) end - def test_errors_full_messages_translates_human_attribute_name_for_model_attributes - @topic.errors.instance_variable_set :@errors, { 'title' => ['empty'] } - I18n.expects(:translate).with(:"topic.title", :default => ['Title'], :scope => [:activerecord, :attributes], :count => 1).returns('Title') - @topic.errors.full_messages :locale => 'en' + def assert_default_message_translation(model, attribute, type) + store_translations(:errors => { :messages => { type => 'default message' } }) + yield + model.valid? + assert_equal 'default message', model.errors.on(attribute) end - # ActiveRecord::Validations - # validates_confirmation_of w/ mocha - def test_validates_confirmation_of_generates_message - Topic.validates_confirmation_of :title - @topic.title_confirmation = 'foo' - @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => nil}) - @topic.valid? + def unique_topic + @unique ||= Topic.create(:title => 'unique!') end - def test_validates_confirmation_of_generates_message_with_custom_default_message - Topic.validates_confirmation_of :title, :message => 'custom' - @topic.title_confirmation = 'foo' - @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => 'custom'}) - @topic.valid? + def replied_topic + @replied_topic ||= begin + topic = Topic.create(:title => "topic") + topic.replies << Reply.new + topic + end end - # validates_acceptance_of w/ mocha + # validates_confirmation_of - def test_validates_acceptance_of_generates_message - Topic.validates_acceptance_of :title, :allow_nil => false - @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => nil}) - @topic.valid? - end - - def test_validates_acceptance_of_generates_message_with_custom_default_message - Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false - @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'}) - @topic.valid? + test "#validates_confirmation_of given no custom message" do + expect_error_added(@topic, :title, :confirmation, :default => nil) do + Topic.validates_confirmation_of :title + @topic.title = 'title' + @topic.title_confirmation = 'foo' + end end - # validates_presence_of w/ mocha - - def test_validates_presence_of_generates_message - Topic.validates_presence_of :title - @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) - @topic.valid? + test "#validates_confirmation_of given a custom message" do + expect_error_added(@topic, :title, :confirmation, :default => 'custom') do + Topic.validates_confirmation_of :title, :message => 'custom' + @topic.title_confirmation = 'foo' + end end - def test_validates_presence_of_generates_message_with_custom_default_message - Topic.validates_presence_of :title, :message => 'custom' - @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) - @topic.valid? + test "#validates_confirmation_of finds the correct message translations" do + assert_message_translations(@topic, :title, :confirmation) do + Topic.validates_confirmation_of :title + @topic.title_confirmation = 'foo' + end end - def test_validates_length_of_within_generates_message_with_title_too_short - Topic.validates_length_of :title, :within => 3..5 - @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) - @topic.valid? - end + # validates_acceptance_of - def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message - Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom' - @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'}) - @topic.valid? + test "#validates_acceptance_of given no custom message" do + expect_error_added(@topic, :title, :accepted, :default => nil) do + Topic.validates_acceptance_of :title, :allow_nil => false + end end - def test_validates_length_of_within_generates_message_with_title_too_long - Topic.validates_length_of :title, :within => 3..5 - @topic.title = 'this title is too long' - @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil}) - @topic.valid? + test "#validates_acceptance_of given a custom message" do + expect_error_added(@topic, :title, :accepted, :default => 'custom') do + Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false + end end - def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message - Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom' - @topic.title = 'this title is too long' - @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'}) - @topic.valid? + test "#validates_acceptance_of finds the correct message translations" do + assert_message_translations(@topic, :title, :accepted) do + Topic.validates_acceptance_of :title, :allow_nil => false + end end - # validates_length_of :within w/ mocha - - def test_validates_length_of_within_generates_message_with_title_too_short - Topic.validates_length_of :title, :within => 3..5 - @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) - @topic.valid? - end + # validates_presence_of - def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message - Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom' - @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'}) - @topic.valid? + test "#validates_presence_of given no custom message" do + expect_error_added(@topic, :title, :blank, :default => nil) do + Topic.validates_presence_of :title + end end - def test_validates_length_of_within_generates_message_with_title_too_long - Topic.validates_length_of :title, :within => 3..5 - @topic.title = 'this title is too long' - @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil}) - @topic.valid? + test "#validates_presence_of given a custom message" do + expect_error_added(@topic, :title, :blank, :default => 'custom') do + Topic.validates_presence_of :title, :message => 'custom' + end end - def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message - Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom' - @topic.title = 'this title is too long' - @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'}) - @topic.valid? + test "#validates_presence_of finds the correct message translations" do + assert_message_translations(@topic, :title, :blank) do + Topic.validates_presence_of :title + end end - # validates_length_of :is w/ mocha + # validates_length_of :too_short - def test_validates_length_of_is_generates_message - Topic.validates_length_of :title, :is => 5 - @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => nil}) - @topic.valid? + test "#validates_length_of (:too_short) and no custom message" do + expect_error_added(@topic, :title, :too_short, :default => nil, :count => 3) do + Topic.validates_length_of :title, :within => 3..5 + end end - def test_validates_length_of_is_generates_message_with_custom_default_message - Topic.validates_length_of :title, :is => 5, :message => 'custom' - @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => 'custom'}) - @topic.valid? + test "#validates_length_of (:too_short) and a custom message" do + expect_error_added(@topic, :title, :too_short, :default => 'custom', :count => 3) do + Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom' + end end - # validates_uniqueness_of w/ mocha - - def test_validates_uniqueness_of_generates_message - Topic.validates_uniqueness_of :title - @topic.title = unique_topic.title - @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil, :value => 'unique!'}) - @topic.valid? + test "#validates_length_of (:too_short) finds the correct message translations" do + assert_message_translations(@topic, :title, :too_short) do + Topic.validates_length_of :title, :within => 3..5 + end end - def test_validates_uniqueness_of_generates_message_with_custom_default_message - Topic.validates_uniqueness_of :title, :message => 'custom' - @topic.title = unique_topic.title - @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom', :value => 'unique!'}) - @topic.valid? - end + # validates_length_of :too_long - # validates_format_of w/ mocha + test "#validates_length_of (:too_long) and no custom message" do + expect_error_added(@topic, :title, :too_long, :default => nil, :count => 5) do + Topic.validates_length_of :title, :within => 3..5 + @topic.title = 'this title is too long' + end + end - def test_validates_format_of_generates_message - Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ - @topic.title = '72x' - @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => nil}) - @topic.valid? + test "#validates_length_of (:too_long) and a custom message" do + expect_error_added(@topic, :title, :too_long, :default => 'custom', :count => 5) do + Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom' + @topic.title = 'this title is too long' + end end - def test_validates_format_of_generates_message_with_custom_default_message - Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom' - @topic.title = '72x' - @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => 'custom'}) - @topic.valid? + test "#validates_length_of (:too_long) finds the correct message translations" do + assert_message_translations(@topic, :title, :too_long) do + Topic.validates_length_of :title, :within => 3..5 + @topic.title = 'this title is too long' + end end - # validates_inclusion_of w/ mocha + # validates_length_of :is - def test_validates_inclusion_of_generates_message - Topic.validates_inclusion_of :title, :in => %w(a b c) - @topic.title = 'z' - @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => nil}) - @topic.valid? + test "#validates_length_of (:is) and no custom message" do + expect_error_added(@topic, :title, :wrong_length, :default => nil, :count => 5) do + Topic.validates_length_of :title, :is => 5 + @topic.title = 'this title has the wrong length' + end end - def test_validates_inclusion_of_generates_message_with_custom_default_message - Topic.validates_inclusion_of :title, :in => %w(a b c), :message => 'custom' - @topic.title = 'z' - @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => 'custom'}) - @topic.valid? + test "#validates_length_of (:is) and a custom message" do + expect_error_added(@topic, :title, :wrong_length, :default => 'custom', :count => 5) do + Topic.validates_length_of :title, :is => 5, :wrong_length => 'custom' + @topic.title = 'this title has the wrong length' + end end - # validates_exclusion_of w/ mocha - - def test_validates_exclusion_of_generates_message - Topic.validates_exclusion_of :title, :in => %w(a b c) - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => nil}) - @topic.valid? + test "#validates_length_of (:is) finds the correct message translations" do + assert_message_translations(@topic, :title, :wrong_length) do + Topic.validates_length_of :title, :is => 5 + @topic.title = 'this title has the wrong length' + end end - def test_validates_exclusion_of_generates_message_with_custom_default_message - Topic.validates_exclusion_of :title, :in => %w(a b c), :message => 'custom' - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => 'custom'}) - @topic.valid? - end + # validates_uniqueness_of - # validates_numericality_of without :only_integer w/ mocha + test "#validates_uniqueness_of and no custom message" do + expect_error_added(@topic, :title, :taken, :default => nil, :value => 'unique!') do + Topic.validates_uniqueness_of :title + @topic.title = unique_topic.title + end + end - def test_validates_numericality_of_generates_message - Topic.validates_numericality_of :title - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil}) - @topic.valid? + test "#validates_uniqueness_of and a custom message" do + expect_error_added(@topic, :title, :taken, :default => 'custom', :value => 'unique!') do + Topic.validates_uniqueness_of :title, :message => 'custom' + @topic.title = unique_topic.title + end end - def test_validates_numericality_of_generates_message_with_custom_default_message - Topic.validates_numericality_of :title, :message => 'custom' - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) - @topic.valid? + test "#validates_uniqueness_of finds the correct message translations" do + assert_message_translations(@topic, :title, :taken) do + Topic.validates_uniqueness_of :title + @topic.title = unique_topic.title + end end - # validates_numericality_of with :only_integer w/ mocha + # validates_format_of - def test_validates_numericality_of_only_integer_generates_message - Topic.validates_numericality_of :title, :only_integer => true - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil}) - @topic.valid? + test "#validates_format_of and no custom message" do + expect_error_added(@topic, :title, :invalid, :default => nil, :value => '72x') do + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ + @topic.title = '72x' + end end - def test_validates_numericality_of_only_integer_generates_message_with_custom_default_message - Topic.validates_numericality_of :title, :only_integer => true, :message => 'custom' - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) - @topic.valid? + test "#validates_format_of and a custom message" do + expect_error_added(@topic, :title, :invalid, :default => 'custom', :value => '72x') do + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom' + @topic.title = '72x' + end end - # validates_numericality_of :odd w/ mocha - - def test_validates_numericality_of_odd_generates_message - Topic.validates_numericality_of :title, :only_integer => true, :odd => true - @topic.title = 0 - @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => nil}) - @topic.valid? + test "#validates_format_of finds the correct message translations" do + assert_message_translations(@topic, :title, :invalid) do + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ + @topic.title = '72x' + end end - def test_validates_numericality_of_odd_generates_message_with_custom_default_message - Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom' - @topic.title = 0 - @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => 'custom'}) - @topic.valid? - end + # validates_inclusion_of - # validates_numericality_of :less_than w/ mocha + test "#validates_inclusion_of and no custom message" do + list = %w(a b c) + expect_error_added(@topic, :title, :inclusion, :default => nil, :value => 'z') do + Topic.validates_inclusion_of :title, :in => list + @topic.title = 'z' + end + end - def test_validates_numericality_of_less_than_generates_message - Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 - @topic.title = 1 - @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => nil}) - @topic.valid? + test "#validates_inclusion_of and a custom message" do + list = %w(a b c) + expect_error_added(@topic, :title, :inclusion, :default => 'custom', :value => 'z') do + Topic.validates_inclusion_of :title, :in => list, :message => 'custom' + @topic.title = 'z' + end end - def test_validates_numericality_of_odd_generates_message_with_custom_default_message - Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom' - @topic.title = 1 - @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'}) - @topic.valid? + test "#validates_inclusion_of finds the correct message translations" do + list = %w(a b c) + assert_message_translations(@topic, :title, :inclusion) do + Topic.validates_inclusion_of :title, :in => list + @topic.title = 'z' + end end - # validates_associated w/ mocha + # validates_exclusion_of - def test_validates_associated_generates_message - Topic.validates_associated :replies - replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) - replied_topic.valid? + test "#validates_exclusion_of and no custom message" do + list = %w(a b c) + expect_error_added(@topic, :title, :exclusion, :default => nil, :value => 'a') do + Topic.validates_exclusion_of :title, :in => list + @topic.title = 'a' + end end - def test_validates_associated_generates_message_with_custom_default_message - Topic.validates_associated :replies - replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) - replied_topic.valid? + test "#validates_exclusion_of and a custom message" do + list = %w(a b c) + expect_error_added(@topic, :title, :exclusion, :default => 'custom', :value => 'a') do + Topic.validates_exclusion_of :title, :in => list, :message => 'custom' + @topic.title = 'a' + end end - # validates_confirmation_of w/o mocha + test "#validates_exclusion_of finds the correct message translations" do + list = %w(a b c) + assert_message_translations(@topic, :title, :exclusion) do + Topic.validates_exclusion_of :title, :in => list + @topic.title = 'a' + end + end - def test_validates_confirmation_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:confirmation => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}} + # validates_numericality_of :not_a_number, without :only_integer - Topic.validates_confirmation_of :title - @topic.title_confirmation = 'foo' - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + test "#validates_numericality_of (:not_a_number, w/o :only_integer) no custom message" do + expect_error_added(@topic, :title, :not_a_number, :default => nil, :value => 'a') do + Topic.validates_numericality_of :title + @topic.title = 'a' + end end - def test_validates_confirmation_of_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:confirmation => 'global message'}}} - - Topic.validates_confirmation_of :title - @topic.title_confirmation = 'foo' - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + test "#validates_numericality_of (:not_a_number, w/o :only_integer) and a custom message" do + expect_error_added(@topic, :title, :not_a_number, :default => 'custom', :value => 'a') do + Topic.validates_numericality_of :title, :message => 'custom' + @topic.title = 'a' + end end - # validates_acceptance_of w/o mocha + test "#validates_numericality_of (:not_a_number, w/o :only_integer) finds the correct message translations" do + assert_message_translations(@topic, :title, :not_a_number) do + Topic.validates_numericality_of :title + @topic.title = 'a' + end + end - def test_validates_acceptance_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:accepted => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}} + # validates_numericality_of :not_a_number, with :only_integer - Topic.validates_acceptance_of :title, :allow_nil => false - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + test "#validates_numericality_of (:not_a_number, with :only_integer) no custom message" do + expect_error_added(@topic, :title, :not_a_number, :default => nil, :value => 'a') do + Topic.validates_numericality_of :title, :only_integer => true + @topic.title = 'a' + end end - def test_validates_acceptance_of_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:accepted => 'global message'}}} - - Topic.validates_acceptance_of :title, :allow_nil => false - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + test "#validates_numericality_of (:not_a_number, with :only_integer) and a custom message" do + expect_error_added(@topic, :title, :not_a_number, :default => 'custom', :value => 'a') do + Topic.validates_numericality_of :title, :only_integer => true, :message => 'custom' + @topic.title = 'a' + end end - # validates_presence_of w/o mocha + test "#validates_numericality_of (:not_a_number, with :only_integer) finds the correct message translations" do + assert_message_translations(@topic, :title, :not_a_number) do + Topic.validates_numericality_of :title, :only_integer => true + @topic.title = 'a' + end + end - def test_validates_presence_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:blank => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:blank => 'global message'}}} + # validates_numericality_of :odd - Topic.validates_presence_of :title - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + test "#validates_numericality_of (:odd) no custom message" do + expect_error_added(@topic, :title, :odd, :default => nil, :value => 0) do + Topic.validates_numericality_of :title, :only_integer => true, :odd => true + @topic.title = 0 + end end - def test_validates_presence_of_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:blank => 'global message'}}} - - Topic.validates_presence_of :title - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + test "#validates_numericality_of (:odd) and a custom message" do + expect_error_added(@topic, :title, :odd, :default => 'custom', :value => 0) do + Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom' + @topic.title = 0 + end end - # validates_length_of :within w/o mocha + test "#validates_numericality_of (:odd) finds the correct message translations" do + assert_message_translations(@topic, :title, :odd) do + Topic.validates_numericality_of :title, :only_integer => true, :odd => true + @topic.title = 0 + end + end - def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:too_short => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}} + # validates_numericality_of :even - Topic.validates_length_of :title, :within => 3..5 - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + test "#validates_numericality_of (:even) no custom message" do + expect_error_added(@topic, :title, :even, :default => nil, :value => 1) do + Topic.validates_numericality_of :title, :only_integer => true, :even => true + @topic.title = 1 + end end - def test_validates_length_of_within_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:too_short => 'global message'}}} - - Topic.validates_length_of :title, :within => 3..5 - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + test "#validates_numericality_of (:even) and a custom message" do + expect_error_added(@topic, :title, :even, :default => 'custom', :value => 1) do + Topic.validates_numericality_of :title, :only_integer => true, :even => true, :message => 'custom' + @topic.title = 1 + end end - # validates_length_of :is w/o mocha + test "#validates_numericality_of (:even) finds the correct message translations" do + assert_message_translations(@topic, :title, :even) do + Topic.validates_numericality_of :title, :only_integer => true, :even => true + @topic.title = 1 + end + end - def test_validates_length_of_is_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} + # validates_numericality_of :less_than - Topic.validates_length_of :title, :is => 5 - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + test "#validates_numericality_of (:less_than) no custom message" do + expect_error_added(@topic, :title, :less_than, :default => nil, :value => 1, :count => 0) do + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 + @topic.title = 1 + end end - def test_validates_length_of_is_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} - - Topic.validates_length_of :title, :is => 5 - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + test "#validates_numericality_of (:less_than) and a custom message" do + expect_error_added(@topic, :title, :less_than, :default => 'custom', :value => 1, :count => 0) do + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom' + @topic.title = 1 + end end - # validates_uniqueness_of w/o mocha + test "#validates_numericality_of (:less_than) finds the correct message translations" do + assert_message_translations(@topic, :title, :less_than) do + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 + @topic.title = 1 + end + end - def test_validates_length_of_is_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} + # validates_associated - Topic.validates_length_of :title, :is => 5 - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + test "#validates_associated no custom message" do + expect_error_added(replied_topic, :replies, :invalid, :default => nil, :value => replied_topic.replies) do + Topic.validates_associated :replies + end end - def test_validates_length_of_is_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} + test "#validates_associated and a custom message" do + expect_error_added(replied_topic, :replies, :invalid, :default => 'custom', :value => replied_topic.replies) do + Topic.validates_associated :replies, :message => 'custom' + end + end - Topic.validates_length_of :title, :is => 5 - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + test "#validates_associated finds the correct message translations" do + assert_message_translations(replied_topic, :replies, :invalid) do + Topic.validates_associated :replies + end end +end - # validates_format_of w/o mocha +# ACTIVERECORD ERROR +# +# * test that it passes given interpolation arguments, the human model name and human attribute name +# * test that it looks messages up with the the correct keys +# * test that it looks up the correct default messages - def test_validates_format_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:invalid => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} +class ActiveRecordErrorI18nTests < ActiveSupport::TestCase + include ActiveRecordValidationsI18nTestHelper - Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + def setup + @reply = Reply.new + @old_backend, I18n.backend = I18n.backend, I18n::Backend::Simple.new end - def test_validates_format_of_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} - - Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + def teardown + I18n.backend = @old_backend + I18n.locale = nil end - # validates_inclusion_of w/o mocha - - def test_validates_inclusion_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:inclusion => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}} + def assert_error_message(message, *args) + assert_equal message, ActiveRecord::Error.new(@reply, *args).message + end - Topic.validates_inclusion_of :title, :in => %w(a b c) - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + def assert_full_message(message, *args) + assert_equal message, ActiveRecord::Error.new(@reply, *args).full_message end - def test_validates_inclusion_of_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:inclusion => 'global message'}}} + test "#generate_message passes the model attribute value for interpolation" do + store_translations(:errors => { :messages => { :foo => "You fooed: {{value}}." } }) + @reply.title = "da title" + assert_error_message 'You fooed: da title.', :title, :foo + end - Topic.validates_inclusion_of :title, :in => %w(a b c) - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + test "#generate_message passes the human_name of the model for interpolation" do + store_translations( + :errors => { :messages => { :foo => "You fooed: {{model}}." } }, + :models => { :topic => 'da topic' } + ) + assert_error_message 'You fooed: da topic.', :title, :foo end - # validates_exclusion_of w/o mocha + test "#generate_message passes the human_name of the attribute for interpolation" do + store_translations( + :errors => { :messages => { :foo => "You fooed: {{attribute}}." } }, + :attributes => { :topic => { :title => 'da topic title' } } + ) + assert_error_message 'You fooed: da topic title.', :title, :foo + end - def test_validates_exclusion_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:exclusion => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}} + # generate_message will look up the key for the error message (e.g. :blank) in these namespaces: + # + # activerecord.errors.models.reply.attributes.title + # activerecord.errors.models.reply + # activerecord.errors.models.topic.attributes.title + # activerecord.errors.models.topic + # [default from class level :validates_foo statement if this is a String] + # activerecord.errors.messages - Topic.validates_exclusion_of :title, :in => %w(a b c) - @topic.title = 'a' - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) - end + test "#generate_message key fallbacks (given a String as key)" do + store_translations( + :errors => { + :models => { + :reply => { + :attributes => { :title => { :custom => 'activerecord.errors.models.reply.attributes.title.custom' } }, + :custom => 'activerecord.errors.models.reply.custom' + }, + :topic => { + :attributes => { :title => { :custom => 'activerecord.errors.models.topic.attributes.title.custom' } }, + :custom => 'activerecord.errors.models.topic.custom' + } + }, + :messages => { + :custom => 'activerecord.errors.messages.custom', + :kaputt => 'activerecord.errors.messages.kaputt' + } + } + ) - def test_validates_exclusion_of_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:exclusion => 'global message'}}} + assert_error_message 'activerecord.errors.models.reply.attributes.title.custom', :title, :kaputt, :message => 'custom' + delete_translation :'activerecord.errors.models.reply.attributes.title.custom' - Topic.validates_exclusion_of :title, :in => %w(a b c) - @topic.title = 'a' - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) - end + assert_error_message 'activerecord.errors.models.reply.custom', :title, :kaputt, :message => 'custom' + delete_translation :'activerecord.errors.models.reply.custom' - # validates_numericality_of without :only_integer w/o mocha + assert_error_message 'activerecord.errors.models.topic.attributes.title.custom', :title, :kaputt, :message => 'custom' + delete_translation :'activerecord.errors.models.topic.attributes.title.custom' - def test_validates_numericality_of_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} + assert_error_message 'activerecord.errors.models.topic.custom', :title, :kaputt, :message => 'custom' + delete_translation :'activerecord.errors.models.topic.custom' - Topic.validates_numericality_of :title - @topic.title = 'a' - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) - end + assert_error_message 'activerecord.errors.messages.custom', :title, :kaputt, :message => 'custom' + delete_translation :'activerecord.errors.messages.custom' - def test_validates_numericality_of_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} + # Implementing this would clash with the AR default behaviour of using validates_foo :message => 'foo' + # as an untranslated string. I.e. at this point we can either fall back to the given string from the + # class-level macro (validates_*) or fall back to the default message for this validation type. + # assert_error_message 'activerecord.errors.messages.kaputt', :title, :kaputt, :message => 'custom' - Topic.validates_numericality_of :title, :only_integer => true - @topic.title = 'a' - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_error_message 'custom', :title, :kaputt, :message => 'custom' end - # validates_numericality_of with :only_integer w/o mocha + test "#generate_message key fallbacks (given a Symbol as key)" do + store_translations( + :errors => { + :models => { + :reply => { + :attributes => { :title => { :kaputt => 'activerecord.errors.models.reply.attributes.title.kaputt' } }, + :kaputt => 'activerecord.errors.models.reply.kaputt' + }, + :topic => { + :attributes => { :title => { :kaputt => 'activerecord.errors.models.topic.attributes.title.kaputt' } }, + :kaputt => 'activerecord.errors.models.topic.kaputt' + } + }, + :messages => { + :kaputt => 'activerecord.errors.messages.kaputt' + } + } + ) - def test_validates_numericality_of_only_integer_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:not_a_number => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} + assert_error_message 'activerecord.errors.models.reply.attributes.title.kaputt', :title, :kaputt + delete_translation :'activerecord.errors.models.reply.attributes.title.kaputt' - Topic.validates_numericality_of :title, :only_integer => true - @topic.title = 'a' - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) - end + assert_error_message 'activerecord.errors.models.reply.kaputt', :title, :kaputt + delete_translation :'activerecord.errors.models.reply.kaputt' + + assert_error_message 'activerecord.errors.models.topic.attributes.title.kaputt', :title, :kaputt + delete_translation :'activerecord.errors.models.topic.attributes.title.kaputt' - def test_validates_numericality_of_only_integer_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:not_a_number => 'global message'}}} + assert_error_message 'activerecord.errors.models.topic.kaputt', :title, :kaputt + delete_translation :'activerecord.errors.models.topic.kaputt' - Topic.validates_numericality_of :title, :only_integer => true - @topic.title = 'a' - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_error_message 'activerecord.errors.messages.kaputt', :title, :kaputt end - # validates_numericality_of :odd w/o mocha + # full_messages - def test_validates_numericality_of_odd_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:odd => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:odd => 'global message'}}} + test "#full_message with no format present" do + store_translations(:errors => { :messages => { :kaputt => 'is kaputt' } }) + assert_full_message 'Title is kaputt', :title, :kaputt + end - Topic.validates_numericality_of :title, :only_integer => true, :odd => true - @topic.title = 0 - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + test "#full_message with a format present" do + store_translations(:errors => { :messages => { :kaputt => 'is kaputt' }, :full_messages => { :format => '{{attribute}}: {{message}}' } }) + assert_full_message 'Title: is kaputt', :title, :kaputt end - def test_validates_numericality_of_odd_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:odd => 'global message'}}} + test "#full_message with a type specific format present" do + store_translations(:errors => { :messages => { :kaputt => 'is kaputt' }, :full_messages => { :kaputt => '{{attribute}} {{message}}!' } }) + assert_full_message 'Title is kaputt!', :title, :kaputt + end - Topic.validates_numericality_of :title, :only_integer => true, :odd => true - @topic.title = 0 - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + test "#full_message with class-level specified custom message" do + store_translations(:errors => { :messages => { :broken => 'is kaputt' }, :full_messages => { :broken => '{{attribute}} {{message}}?!' } }) + assert_full_message 'Title is kaputt?!', :title, :kaputt, :message => :broken end - # validates_numericality_of :less_than w/o mocha + # switch locales - def test_validates_numericality_of_less_than_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:less_than => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}} + test "#message allows to switch locales" do + store_translations(:en, :errors => { :messages => { :kaputt => 'is kaputt' } }) + store_translations(:de, :errors => { :messages => { :kaputt => 'ist kaputt' } }) - Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 - @topic.title = 1 - @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_error_message 'is kaputt', :title, :kaputt + I18n.locale = :de + assert_error_message 'ist kaputt', :title, :kaputt + I18n.locale = :en + assert_error_message 'is kaputt', :title, :kaputt end - def test_validates_numericality_of_less_than_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:less_than => 'global message'}}} + test "#full_message allows to switch locales" do + store_translations(:en, :errors => { :messages => { :kaputt => 'is kaputt' } }, :attributes => { :topic => { :title => 'The title' } }) + store_translations(:de, :errors => { :messages => { :kaputt => 'ist kaputt' } }, :attributes => { :topic => { :title => 'Der Titel' } }) - Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 - @topic.title = 1 - @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_full_message 'The title is kaputt', :title, :kaputt + I18n.locale = :de + assert_full_message 'Der Titel ist kaputt', :title, :kaputt + I18n.locale = :en + assert_full_message 'The title is kaputt', :title, :kaputt end +end +# ACTIVERECORD DEFAULT ERROR MESSAGES +# +# * test that Error generates the default error messages - # validates_associated w/o mocha - - def test_validates_associated_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} +class ActiveRecordDefaultErrorMessagesI18nTests < ActiveSupport::TestCase + def assert_default_error_message(message, *args) + assert_equal message, error_message(*args) + end - Topic.validates_associated :replies - replied_topic.valid? - assert_equal 'custom message', replied_topic.errors.on(:replies) + def error_message(*args) + ActiveRecord::Error.new(Topic.new, :title, *args).message end - def test_validates_associated_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} + # used by: validates_inclusion_of + test "default error message: inclusion" do + assert_default_error_message 'is not included in the list', :inclusion, :value => 'title' + end - Topic.validates_associated :replies - replied_topic.valid? - assert_equal 'global message', replied_topic.errors.on(:replies) + # used by: validates_exclusion_of + test "default error message: exclusion" do + assert_default_error_message 'is reserved', :exclusion, :value => 'title' end - def test_validations_with_message_symbol_must_translate - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:custom_error => "I am a custom error"}}} - Topic.validates_presence_of :title, :message => :custom_error - @topic.title = nil - @topic.valid? - assert_equal "I am a custom error", @topic.errors.on(:title) + # used by: validates_associated and validates_format_of + test "default error message: invalid" do + assert_default_error_message 'is invalid', :invalid, :value => 'title' end - def test_validates_with_message_symbol_must_translate_per_attribute - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:custom_error => "I am a custom error"}}}}}} - Topic.validates_presence_of :title, :message => :custom_error - @topic.title = nil - @topic.valid? - assert_equal "I am a custom error", @topic.errors.on(:title) + # used by: validates_confirmation_of + test "default error message: confirmation" do + assert_default_error_message "doesn't match confirmation", :confirmation, :default => nil end - def test_validates_with_message_symbol_must_translate_per_model - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:custom_error => "I am a custom error"}}}} - Topic.validates_presence_of :title, :message => :custom_error - @topic.title = nil - @topic.valid? - assert_equal "I am a custom error", @topic.errors.on(:title) + # used by: validates_acceptance_of + test "default error message: accepted" do + assert_default_error_message "must be accepted", :accepted end - def test_validates_with_message_string - Topic.validates_presence_of :title, :message => "I am a custom error" - @topic.title = nil - @topic.valid? - assert_equal "I am a custom error", @topic.errors.on(:title) + # used by: add_on_empty + test "default error message: empty" do + assert_default_error_message "can't be empty", :empty end -end -class ActiveRecordValidationsGenerateMessageI18nTests < ActiveSupport::TestCase - def setup - reset_callbacks Topic - @topic = Topic.new - I18n.backend.store_translations :'en', { - :activerecord => { - :errors => { - :messages => { - :inclusion => "is not included in the list", - :exclusion => "is reserved", - :invalid => "is invalid", - :confirmation => "doesn't match confirmation", - :accepted => "must be accepted", - :empty => "can't be empty", - :blank => "can't be blank", - :too_long => "is too long (maximum is {{count}} characters)", - :too_short => "is too short (minimum is {{count}} characters)", - :wrong_length => "is the wrong length (should be {{count}} characters)", - :taken => "has already been taken", - :not_a_number => "is not a number", - :greater_than => "must be greater than {{count}}", - :greater_than_or_equal_to => "must be greater than or equal to {{count}}", - :equal_to => "must be equal to {{count}}", - :less_than => "must be less than {{count}}", - :less_than_or_equal_to => "must be less than or equal to {{count}}", - :odd => "must be odd", - :even => "must be even" - } - } - } - } + # used by: add_on_blank + test "default error message: blank" do + assert_default_error_message "can't be blank", :blank end - def reset_callbacks(*models) - models.each do |model| - model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - end + # used by: validates_length_of + test "default error message: too_long" do + assert_default_error_message "is too long (maximum is 10 characters)", :too_long, :count => 10 end - # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) - def test_generate_message_inclusion_with_default_message - assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title') + # used by: validates_length_of + test "default error message: too_short" do + assert_default_error_message "is too short (minimum is 10 characters)", :too_short, :count => 10 end - def test_generate_message_inclusion_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :inclusion, :default => 'custom message {{value}}', :value => 'title') + # used by: validates_length_of + test "default error message: wrong_length" do + assert_default_error_message "is the wrong length (should be 10 characters)", :wrong_length, :count => 10 end - # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value) - def test_generate_message_exclusion_with_default_message - assert_equal 'is reserved', @topic.errors.generate_message(:title, :exclusion, :default => nil, :value => 'title') + # used by: validates_uniqueness_of + test "default error message: taken" do + assert_default_error_message "has already been taken", :taken, :value => 'title' end - def test_generate_message_exclusion_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :exclusion, :default => 'custom message {{value}}', :value => 'title') + # used by: validates_numericality_of + test "default error message: not_a_number" do + assert_default_error_message "is not a number", :not_a_number, :value => 'title' end - # validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) - # validates_format_of: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) - def test_generate_message_invalid_with_default_message - assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title') + # used by: validates_numericality_of + test "default error message: greater_than" do + assert_default_error_message "must be greater than 10", :greater_than, :value => 'title', :count => 10 end - def test_generate_message_invalid_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title') + # used by: validates_numericality_of + test "default error message: greater_than_or_equal_to" do + assert_default_error_message "must be greater than or equal to 10", :greater_than_or_equal_to, :value => 'title', :count => 10 end - # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message]) - def test_generate_message_confirmation_with_default_message - assert_equal "doesn't match confirmation", @topic.errors.generate_message(:title, :confirmation, :default => nil) + # used by: validates_numericality_of + test "default error message: equal_to" do + assert_default_error_message "must be equal to 10", :equal_to, :value => 'title', :count => 10 end - def test_generate_message_confirmation_with_custom_message - assert_equal 'custom message', @topic.errors.generate_message(:title, :confirmation, :default => 'custom message') + # used by: validates_numericality_of + test "default error message: less_than" do + assert_default_error_message "must be less than 10", :less_than, :value => 'title', :count => 10 end - # validates_acceptance_of: generate_message(attr_name, :accepted, :default => configuration[:message]) - def test_generate_message_accepted_with_default_message - assert_equal "must be accepted", @topic.errors.generate_message(:title, :accepted, :default => nil) + # used by: validates_numericality_of + test "default error message: less_than_or_equal_to" do + assert_default_error_message "must be less than or equal to 10", :less_than_or_equal_to, :value => 'title', :count => 10 end - def test_generate_message_accepted_with_custom_message - assert_equal 'custom message', @topic.errors.generate_message(:title, :accepted, :default => 'custom message') + # used by: validates_numericality_of + test "default error message: odd" do + assert_default_error_message "must be odd", :odd, :value => 'title', :count => 10 end - # add_on_empty: generate_message(attr, :empty, :default => custom_message) - def test_generate_message_empty_with_default_message - assert_equal "can't be empty", @topic.errors.generate_message(:title, :empty, :default => nil) + # used by: validates_numericality_of + test "default error message: even" do + assert_default_error_message "must be even", :even, :value => 'title', :count => 10 end - def test_generate_message_empty_with_custom_message - assert_equal 'custom message', @topic.errors.generate_message(:title, :empty, :default => 'custom message') + test "custom message string interpolation" do + assert_equal 'custom message title', error_message(:invalid, :default => 'custom message {{value}}', :value => 'title') end +end - # add_on_blank: generate_message(attr, :blank, :default => custom_message) - def test_generate_message_blank_with_default_message - assert_equal "can't be blank", @topic.errors.generate_message(:title, :blank, :default => nil) +# ACTIVERECORD VALIDATION ERROR MESSAGES - FULL STACK +# +# * test a few combinations full stack to ensure the tests above are correct + +class I18nPerson < Person +end + +class ActiveRecordValidationsI18nFullStackTests < ActiveSupport::TestCase + include ActiveRecordValidationsI18nTestHelper + + def setup + reset_callbacks(I18nPerson) + @old_backend, I18n.backend = I18n.backend, I18n::Backend::Simple.new + @person = I18nPerson.new end - def test_generate_message_blank_with_custom_message - assert_equal 'custom message', @topic.errors.generate_message(:title, :blank, :default => 'custom message') + def teardown + reset_callbacks(I18nPerson) + I18n.backend = @old_backend end - # validates_length_of: generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end) - def test_generate_message_too_long_with_default_message - assert_equal "is too long (maximum is 10 characters)", @topic.errors.generate_message(:title, :too_long, :default => nil, :count => 10) + def assert_name_invalid(message) + yield + @person.valid? + assert_equal message, @person.errors.on(:name) end - def test_generate_message_too_long_with_custom_message - assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_long, :default => 'custom message {{count}}', :count => 10) + # Symbols as class-level validation messages + + test "Symbol as class level validation message translated per attribute (translation on child class)" do + assert_name_invalid("is broken") do + store_translations :errors => {:models => {:i18n_person => {:attributes => {:name => {:broken => "is broken"}}}}} + I18nPerson.validates_presence_of :name, :message => :broken + end end - # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin) - def test_generate_message_too_short_with_default_message - assert_equal "is too short (minimum is 10 characters)", @topic.errors.generate_message(:title, :too_short, :default => nil, :count => 10) + test "Symbol as class level validation message translated per attribute (translation on base class)" do + assert_name_invalid("is broken") do + store_translations :errors => {:models => {:person => {:attributes => {:name => {:broken => "is broken"}}}}} + I18nPerson.validates_presence_of :name, :message => :broken + end end - def test_generate_message_too_short_with_custom_message - assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_short, :default => 'custom message {{count}}', :count => 10) + test "Symbol as class level validation message translated per model (translation on child class)" do + assert_name_invalid("is broken") do + store_translations :errors => {:models => {:i18n_person => {:broken => "is broken"}}} + I18nPerson.validates_presence_of :name, :message => :broken + end end - # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value) - def test_generate_message_wrong_length_with_default_message - assert_equal "is the wrong length (should be 10 characters)", @topic.errors.generate_message(:title, :wrong_length, :default => nil, :count => 10) + test "Symbol as class level validation message translated per model (translation on base class)" do + assert_name_invalid("is broken") do + store_translations :errors => {:models => {:person => {:broken => "is broken"}}} + I18nPerson.validates_presence_of :name, :message => :broken + end end - def test_generate_message_wrong_length_with_custom_message - assert_equal 'custom message 10', @topic.errors.generate_message(:title, :wrong_length, :default => 'custom message {{count}}', :count => 10) + test "Symbol as class level validation message translated as error message" do + assert_name_invalid("is broken") do + store_translations :errors => {:messages => {:broken => "is broken"}} + I18nPerson.validates_presence_of :name, :message => :broken + end end - # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message]) - def test_generate_message_taken_with_default_message - assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title') + # Strings as class-level validation messages + + test "String as class level validation message translated per attribute (translation on child class)" do + assert_name_invalid("is broken") do + store_translations :errors => {:models => {:i18n_person => {:attributes => {:name => {"is broken" => "is broken"}}}}} + I18nPerson.validates_presence_of :name, :message => "is broken" + end end - def test_generate_message_taken_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message {{value}}', :value => 'title') + test "String as class level validation message translated per attribute (translation on base class)" do + assert_name_invalid("is broken") do + store_translations :errors => {:models => {:person => {:attributes => {:name => {"is broken" => "is broken"}}}}} + I18nPerson.validates_presence_of :name, :message => "is broken" + end end - # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) - def test_generate_message_not_a_number_with_default_message - assert_equal "is not a number", @topic.errors.generate_message(:title, :not_a_number, :default => nil, :value => 'title') + test "String as class level validation message translated per model (translation on child class)" do + assert_name_invalid("is broken") do + store_translations :errors => {:models => {:i18n_person => {"is broken" => "is broken"}}} + I18nPerson.validates_presence_of :name, :message => "is broken" + end end - def test_generate_message_not_a_number_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :not_a_number, :default => 'custom message {{value}}', :value => 'title') + test "String as class level validation message translated per model (translation on base class)" do + assert_name_invalid("is broken") do + store_translations :errors => {:models => {:person => {"is broken" => "is broken"}}} + I18nPerson.validates_presence_of :name, :message => "is broken" + end end - # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message]) - def test_generate_message_greater_than_with_default_message - assert_equal "must be greater than 10", @topic.errors.generate_message(:title, :greater_than, :default => nil, :value => 'title', :count => 10) + test "String as class level validation message translated as error message" do + assert_name_invalid("is broken") do + store_translations :errors => {:messages => {"is broken" => "is broken"}} + I18nPerson.validates_presence_of :name, :message => "is broken" + end end - def test_generate_message_greater_than_or_equal_to_with_default_message - assert_equal "must be greater than or equal to 10", @topic.errors.generate_message(:title, :greater_than_or_equal_to, :default => nil, :value => 'title', :count => 10) + test "String as class level validation message not translated (uses message as default)" do + assert_name_invalid("is broken!") do + I18nPerson.validates_presence_of :name, :message => "is broken!" + end end +end - def test_generate_message_equal_to_with_default_message - assert_equal "must be equal to 10", @topic.errors.generate_message(:title, :equal_to, :default => nil, :value => 'title', :count => 10) +class ActiveRecordValidationsI18nFullMessagesFullStackTests < ActiveSupport::TestCase + include ActiveRecordValidationsI18nTestHelper + + def setup + reset_callbacks(I18nPerson) + @old_backend, I18n.backend = I18n.backend, I18n::Backend::Simple.new + @person = I18nPerson.new end - def test_generate_message_less_than_with_default_message - assert_equal "must be less than 10", @topic.errors.generate_message(:title, :less_than, :default => nil, :value => 'title', :count => 10) + def teardown + reset_callbacks(I18nPerson) + I18n.backend = @old_backend end - def test_generate_message_less_than_or_equal_to_with_default_message - assert_equal "must be less than or equal to 10", @topic.errors.generate_message(:title, :less_than_or_equal_to, :default => nil, :value => 'title', :count => 10) + def assert_full_message(message) + yield + @person.valid? + assert_equal message, @person.errors.full_messages.join end - def test_generate_message_odd_with_default_message - assert_equal "must be odd", @topic.errors.generate_message(:title, :odd, :default => nil, :value => 'title', :count => 10) + test "full_message format stored per custom error message key" do + assert_full_message("Name is broken!") do + store_translations :errors => { :messages => { :broken => 'is broken' }, :full_messages => { :broken => '{{attribute}} {{message}}!' } } + I18nPerson.validates_presence_of :name, :message => :broken + end end - def test_generate_message_even_with_default_message - assert_equal "must be even", @topic.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10) + test "full_message format stored per error type" do + assert_full_message("Name can't be blank!") do + store_translations :errors => { :full_messages => { :blank => '{{attribute}} {{message}}!' } } + I18nPerson.validates_presence_of :name + end end # ActiveRecord#RecordInvalid exception + test "full_message format stored as default" do + assert_full_message("Name: can't be blank") do + store_translations :errors => { :full_messages => { :format => '{{attribute}}: {{message}}' } } + I18nPerson.validates_presence_of :name + end + end test "RecordInvalid exception can be localized" do topic = Topic.new topic.errors.add(:title, :invalid) -- 1.6.4.2 From 70ed47f5b41f3e99f96e14da4299af260a6bcb79 Mon Sep 17 00:00:00 2001 From: Chad Woolley Date: Sat, 29 Aug 2009 23:35:30 -0700 Subject: [PATCH 270/779] CI config updates: do not send CI emails unless explicitly enabled, use 'gem update --system', and send emails from an address which can post to the core list [#3116 state:committed] Signed-off-by: Jeremy Kemper --- ci/cruise_config.rb | 10 +++++++--- 1 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ci/cruise_config.rb b/ci/cruise_config.rb index 15a1bd6..9c7fa98 100644 --- a/ci/cruise_config.rb +++ b/ci/cruise_config.rb @@ -1,5 +1,9 @@ Project.configure do |project| - project.build_command = 'ruby ci/ci_build.rb' - project.email_notifier.emails = ['rubyonrails-core@googlegroups.com', 'thewoolleyman@gmail.com','michael@koziarski.com', 'david@loudthinking.com', 'jeremy@bitsweat.net', 'josh@joshpeek.com', 'pratiknaik@gmail.com', 'wycats@gmail.com'] - project.email_notifier.from = 'thewoolleyman+railsci@gmail.com' + # Send email notifications about broken and fixed builds to core mailing list + if Socket.gethostname =~ /ci.rubyonrails.org/ && ENV['ENABLE_RAILS_CI_EMAILS'] == 'true' + project.email_notifier.emails = ['rubyonrails-core@googlegroups.com'] + end + + project.build_command = 'sudo gem update --system && ruby ci/ci_build.rb' + project.email_notifier.from = 'thewoolleyman@gmail.com' end -- 1.6.4.2 From ff8cb50f25e426c531743fbf918e2a870e60e0e8 Mon Sep 17 00:00:00 2001 From: Jay Pignata Date: Sun, 16 Aug 2009 21:46:26 -0400 Subject: [PATCH 271/779] Ensuring that a singular model name is set for use in controllers when scaffold is passed a plural model name [#3062 state:committed] Signed-off-by: Jeremy Kemper --- .../components/scaffold/scaffold_generator.rb | 1 + .../generators/rails_scaffold_generator_test.rb | 1 + 2 files changed, 2 insertions(+), 0 deletions(-) diff --git a/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb b/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb index 2a5edee..88bc325 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb +++ b/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb @@ -19,6 +19,7 @@ class ScaffoldGenerator < Rails::Generator::NamedBase if @name == @name.pluralize && !options[:force_plural] logger.warning "Plural version of the model detected, using singularized version. Override with --force-plural." @name = @name.singularize + assign_names!(@name) end @controller_name = @name.pluralize diff --git a/railties/test/generators/rails_scaffold_generator_test.rb b/railties/test/generators/rails_scaffold_generator_test.rb index 70829a7..2c60eb0 100644 --- a/railties/test/generators/rails_scaffold_generator_test.rb +++ b/railties/test/generators/rails_scaffold_generator_test.rb @@ -116,6 +116,7 @@ class RailsScaffoldGeneratorTest < GeneratorTestCase assert_equal "product_lines", g.controller_plural_name assert_equal "product_lines", g.controller_file_name assert_equal "product_lines", g.controller_table_name + assert_equal "ProductLine", g.class_name end def test_scaffold_plural_model_name_without_force_plural_generates_singular_model -- 1.6.4.2 From 49342d1745dd0e6c4ebebe9e535e374783ac2c10 Mon Sep 17 00:00:00 2001 From: Antonio Tapiador del Dujo Date: Tue, 24 Mar 2009 14:49:47 +0100 Subject: [PATCH 272/779] I18n support for plugins Rails will now automatically add locale files found in any engine's locale directory to the I18n.load_path (i.e. files that match the glob pattern "config/locales/**/*.{rb,yml}" relative to engine directories). [#2325 state:committed] Signed-off-by: Jeremy Kemper --- railties/CHANGELOG | 6 ++++++ railties/lib/rails/plugin.rb | 12 ++++++++++++ railties/lib/rails/plugin/loader.rb | 7 +++++++ .../plugins/engines/engine/config/locales/en.yml | 2 ++ railties/test/initializer_test.rb | 2 ++ railties/test/plugin_loader_test.rb | 8 ++++++++ 6 files changed, 37 insertions(+), 0 deletions(-) create mode 100644 railties/test/fixtures/plugins/engines/engine/config/locales/en.yml diff --git a/railties/CHANGELOG b/railties/CHANGELOG index b095fab..01ba2b9 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,7 +1,13 @@ +*2.3.4 (pending)* + +* I18n support for plugins. #2325 [Antonio Tapiador, Sven Fuchs] + + *2.3.3 (July 12 2009) * Version bump + *2.3.2 [Final] (March 15, 2009)* * Allow metal to live in plugins #2045 [Matthew Rudy] diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index 80deb73..1dc4d14 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -71,6 +71,10 @@ module Rails File.exist?(routing_file) end + # Returns true if there is any localization file in locale_path + def localized? + locale_files.any? + end def view_path File.join(directory, 'app', 'views') @@ -87,6 +91,14 @@ module Rails def routing_file File.join(directory, 'config', 'routes.rb') end + + def locale_path + File.join(directory, 'config', 'locales') + end + + def locale_files + Dir[ File.join(locale_path, '*.{rb,yml}') ] + end private diff --git a/railties/lib/rails/plugin/loader.rb b/railties/lib/rails/plugin/loader.rb index 66e01d7..49670b3 100644 --- a/railties/lib/rails/plugin/loader.rb +++ b/railties/lib/rails/plugin/loader.rb @@ -73,6 +73,7 @@ module Rails def configure_engines if engines.any? add_engine_routing_configurations + add_engine_locales add_engine_controller_paths add_engine_view_paths end @@ -84,6 +85,12 @@ module Rails end end + def add_engine_locales + # reverse it such that the last engine can overwrite translations from the first, like with routes + locale_files = engines.select(&:localized?).collect(&:locale_files).reverse.flatten + I18n.load_path += locale_files - I18n.load_path + end + def add_engine_controller_paths ActionController::Routing.controller_paths += engines.collect(&:controller_path) end diff --git a/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml b/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml new file mode 100644 index 0000000..641a7e0 --- /dev/null +++ b/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml @@ -0,0 +1,2 @@ +en: + hello: "Hello from Engine" diff --git a/railties/test/initializer_test.rb b/railties/test/initializer_test.rb index ace0cca..68c99ee 100644 --- a/railties/test/initializer_test.rb +++ b/railties/test/initializer_test.rb @@ -313,6 +313,8 @@ class InitializerSetupI18nTests < Test::Unit::TestCase File.expand_path(File.dirname(__FILE__) + "/../../activesupport/lib/active_support/locale/en.yml"), File.expand_path(File.dirname(__FILE__) + "/../../actionpack/lib/action_view/locale/en.yml"), File.expand_path(File.dirname(__FILE__) + "/../../activerecord/lib/active_record/locale/en.yml"), + # FIXME: should clean I18n.load_path between each test + File.expand_path(File.dirname(__FILE__) + "/../../railties/test/fixtures/plugins/engines/engine/config/locales/en.yml"), "my/test/locale.yml", "my/other/locale.yml" ], I18n.load_path.collect { |path| path =~ /^\./ ? File.expand_path(path) : path } end diff --git a/railties/test/plugin_loader_test.rb b/railties/test/plugin_loader_test.rb index b270748..b0ab4b7 100644 --- a/railties/test/plugin_loader_test.rb +++ b/railties/test/plugin_loader_test.rb @@ -155,6 +155,14 @@ class TestPluginLoader < Test::Unit::TestCase plugin_load_paths.each { |path| assert $LOAD_PATH.include?(path) } end + def test_should_add_locale_files_to_I18n_load_path + only_load_the_following_plugins! [:engine] + + @loader.send :add_engine_locales + + assert I18n.load_path.include?(File.join(plugin_fixture_path('engines/engine'), 'config', 'locales', 'en.yml')) + end + private def reset_load_path! -- 1.6.4.2 From 49c4a79e59622c74cc9bb1aad04435ab171baf7c Mon Sep 17 00:00:00 2001 From: Jay Pignata Date: Mon, 31 Aug 2009 13:01:34 -0500 Subject: [PATCH 273/779] Duplicating the options hash in Date#advance to prevent modification of the original [#1133 state:resolved] Signed-off-by: Joshua Peek --- .../active_support/core_ext/date/calculations.rb | 1 + activesupport/test/core_ext/date_ext_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 7f94da0..fa7f618 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -92,6 +92,7 @@ module ActiveSupport #:nodoc: # Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with # any of these keys: :years, :months, :weeks, :days. def advance(options) + options = options.dup d = self d = d >> options.delete(:years) * 12 if options[:years] d = d >> options.delete(:months) if options[:months] diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 3a25a85..2d2bbf1 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -250,6 +250,12 @@ class DateExtCalculationsTest < Test::Unit::TestCase Time.zone_default = nil end + def test_date_advance_should_not_change_passed_options_hash + options = { :years => 3, :months => 11, :days => 2 } + Date.new(2005,2,28).advance(options) + assert_equal({ :years => 3, :months => 11, :days => 2 }, options) + end + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz -- 1.6.4.2 From 6fdfe4cb5fa85160f5d91bad64d5c23b4f87f820 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 31 Aug 2009 16:09:47 -0500 Subject: [PATCH 274/779] Deprecated "best fit" detection is to difficult. Just provide a switch to toggle the new behavor on. # new_rails_defaults.rb ActionController::Routing.generate_best_match = false --- actionpack/lib/action_controller/routing.rb | 3 +++ .../lib/action_controller/routing/route_set.rb | 17 ++--------------- actionpack/test/controller/routing_test.rb | 6 ++---- .../configs/initializers/new_rails_defaults.rb | 2 ++ 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index c0eb613..f9b0c4b 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -271,6 +271,9 @@ module ActionController ALLOWED_REQUIREMENTS_FOR_OPTIMISATION = [:controller, :action].to_set + mattr_accessor :generate_best_match + self.generate_best_match = true + # The root paths which may contain controller files mattr_accessor :controller_paths self.controller_paths = [] diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 329ba5b..8e4ed7b 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -406,24 +406,11 @@ module ActionController # don't use the recalled keys when determining which routes to check future_routes, deprecated_routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }] - no_worries = future_routes == deprecated_routes + routes = Routing.generate_best_match ? deprecated_routes : future_routes - deprecated_routes.each_with_index do |route, index| + routes.each_with_index do |route, index| results = route.__send__(method, options, merged, expire_on) if results && (!results.is_a?(Array) || results.first) - - # Compare results with Rails 3.0 behavior - unless no_worries - future_routes.each_with_index do |route2, index2| - new_results = route2.__send__(method, options, merged, expire_on) - if new_results && (!new_results.is_a?(Array) || new_results.first) && index2 < future_routes.index(route) - ActiveSupport::Deprecation.warn "The URL you generated will use the first matching route in routes.rb rather than the \"best\" match. " + - "In Rails 3.0 #{new_results} will be generated instead of #{results}" - break - end - end - end - return results end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index aab7f03..e54f2f7 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -2191,10 +2191,8 @@ class RouteSetTest < ActiveSupport::TestCase map.connect "/ws/people", :controller => "people", :action => "index", :ws => true end - assert_deprecated { - url = set.generate(:controller => "people", :action => "index", :ws => true) - assert_equal "/ws/people", url - } + url = set.generate(:controller => "people", :action => "index", :ws => true) + assert_equal "/ws/people", url end def test_generate_changes_controller_module diff --git a/railties/configs/initializers/new_rails_defaults.rb b/railties/configs/initializers/new_rails_defaults.rb index 8ec3186..c94db0a 100644 --- a/railties/configs/initializers/new_rails_defaults.rb +++ b/railties/configs/initializers/new_rails_defaults.rb @@ -11,6 +11,8 @@ if defined?(ActiveRecord) ActiveRecord::Base.store_full_sti_class = true end +ActionController::Routing.generate_best_match = false + # Use ISO 8601 format for JSON serialized times and dates. ActiveSupport.use_standard_json_time_format = true -- 1.6.4.2 From 6bf17770af2a563b037b961a8dbe84f63f9c929c Mon Sep 17 00:00:00 2001 From: Geoff Buesing Date: Mon, 31 Aug 2009 17:58:54 -0500 Subject: [PATCH 275/779] Rails::Info doesn't require version for unwanted frameworks [#3124 state:committed] Signed-off-by: Jeremy Kemper --- railties/builtin/rails_info/rails/info.rb | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/railties/builtin/rails_info/rails/info.rb b/railties/builtin/rails_info/rails/info.rb index a20d9bf..8c858d2 100644 --- a/railties/builtin/rails_info/rails/info.rb +++ b/railties/builtin/rails_info/rails/info.rb @@ -25,8 +25,10 @@ module Rails end def framework_version(framework) - require "#{framework}/version" - "#{framework.classify}::VERSION::STRING".constantize + if Object.const_defined?(framework.classify) + require "#{framework}/version" + "#{framework.classify}::VERSION::STRING".constantize + end end def edge_rails_revision(info = git_info) -- 1.6.4.2 From 594a281d663dd8a7801651f4aea7e5b7f4f02548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 1 Sep 2009 13:11:15 +0200 Subject: [PATCH 276/779] Assert primary key does not exist in habtm when the association is defined, instead of doing that everytime a record is inserted. [#3128 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/associations.rb | 24 ++++++++++---------- .../has_and_belongs_to_many_association.rb | 16 ------------- .../cases/associations/habtm_join_table_test.rb | 16 +----------- activerecord/test/fixtures/edges.yml | 3 +- activerecord/test/schema/schema.rb | 2 +- 5 files changed, 16 insertions(+), 45 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a08119c..c68c8cc 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -53,6 +53,12 @@ module ActiveRecord end end + class HasAndBelongsToManyAssociationWithPrimaryKeyError < ActiveRecordError #:nodoc: + def initialize(reflection) + super("Primary key is not allowed in a has_and_belongs_to_many join table (#{reflection.options[:join_table]}).") + end + end + class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc: def initialize(reflection) super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.") @@ -1583,16 +1589,19 @@ module ActiveRecord def create_has_and_belongs_to_many_reflection(association_id, options, &extension) options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association) - options[:extend] = create_extension_modules(association_id, extension, options[:extend]) reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self) - + reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name)) + if reflection.association_foreign_key == reflection.primary_key_name raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection) end - reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name)) + if connection.supports_primary_key? && + (connection.primary_key(reflection.options[:join_table]) rescue false) + raise HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection) + end reflection end @@ -1601,15 +1610,6 @@ module ActiveRecord [ associations ].flatten.collect { |association| reflect_on_association(association.to_s.intern) } end - def guard_against_unlimitable_reflections(reflections, options) - if (options[:offset] || options[:limit]) && !using_limitable_reflections?(reflections) - raise( - ConfigurationError, - "You can not use offset and limit together with has_many or has_and_belongs_to_many associations" - ) - end - end - def select_all_rows(options, join_dependency) connection.select_all( construct_finder_sql_with_included_associations(options, join_dependency), diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index cb60baa..af9ce3d 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -1,11 +1,6 @@ module ActiveRecord module Associations class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc: - def initialize(owner, reflection) - super - @primary_key_list = {} - end - def create(attributes = {}) create_record(attributes) { |record| insert_record(record) } end @@ -22,12 +17,6 @@ module ActiveRecord @reflection.reset_column_information end - def has_primary_key? - return @has_primary_key unless @has_primary_key.nil? - @has_primary_key = (ActiveRecord::Base.connection.supports_primary_key? && - ActiveRecord::Base.connection.primary_key(@reflection.options[:join_table])) - end - protected def construct_find_options!(options) options[:joins] = @join_sql @@ -40,11 +29,6 @@ module ActiveRecord end def insert_record(record, force = true, validate = true) - if has_primary_key? - raise ActiveRecord::ConfigurationError, - "Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})." - end - if record.new_record? if force record.save! diff --git a/activerecord/test/cases/associations/habtm_join_table_test.rb b/activerecord/test/cases/associations/habtm_join_table_test.rb index bf3e04c..745f169 100644 --- a/activerecord/test/cases/associations/habtm_join_table_test.rb +++ b/activerecord/test/cases/associations/habtm_join_table_test.rb @@ -36,21 +36,9 @@ class HabtmJoinTableTest < ActiveRecord::TestCase uses_transaction :test_should_raise_exception_when_join_table_has_a_primary_key def test_should_raise_exception_when_join_table_has_a_primary_key if ActiveRecord::Base.connection.supports_primary_key? - assert_raise ActiveRecord::ConfigurationError do - jaime = MyReader.create(:name=>"Jaime") - jaime.my_books << MyBook.create(:name=>'Great Expectations') + assert_raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError do + MyReader.has_and_belongs_to_many :my_books end end end - - uses_transaction :test_should_cache_result_of_primary_key_check - def test_should_cache_result_of_primary_key_check - if ActiveRecord::Base.connection.supports_primary_key? - ActiveRecord::Base.connection.stubs(:primary_key).with('my_books_my_readers').returns(false).once - weaz = MyReader.create(:name=>'Weaz') - - weaz.my_books << MyBook.create(:name=>'Great Expectations') - weaz.my_books << MyBook.create(:name=>'Greater Expectations') - end - end end diff --git a/activerecord/test/fixtures/edges.yml b/activerecord/test/fixtures/edges.yml index c16c70d..b804f7b 100644 --- a/activerecord/test/fixtures/edges.yml +++ b/activerecord/test/fixtures/edges.yml @@ -1,6 +1,5 @@ <% (1..4).each do |id| %> edge_<%= id %>: - id: <%= id %> source_id: <%= id %> sink_id: <%= id + 1 %> -<% end %> \ No newline at end of file +<% end %> diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 87fad0e..da71aac 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -153,7 +153,7 @@ ActiveRecord::Schema.define do t.integer :access_level, :default => 1 end - create_table :edges, :force => true do |t| + create_table :edges, :force => true, :id => false do |t| t.column :source_id, :integer, :null => false t.column :sink_id, :integer, :null => false end -- 1.6.4.2 From 2b82708b0efb3a3458e8177beab58f0c585788ae Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 2 Sep 2009 13:57:33 -0700 Subject: [PATCH 277/779] Revert "Assert primary key does not exist in habtm when the association is defined, instead of doing that everytime a record is inserted." Test failures on PostgreSQL. [#3128] This reverts commit 594a281d663dd8a7801651f4aea7e5b7f4f02548. --- activerecord/lib/active_record/associations.rb | 24 ++++++++++---------- .../has_and_belongs_to_many_association.rb | 16 +++++++++++++ .../cases/associations/habtm_join_table_test.rb | 16 +++++++++++- activerecord/test/fixtures/edges.yml | 3 +- activerecord/test/schema/schema.rb | 2 +- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index c68c8cc..a08119c 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -53,12 +53,6 @@ module ActiveRecord end end - class HasAndBelongsToManyAssociationWithPrimaryKeyError < ActiveRecordError #:nodoc: - def initialize(reflection) - super("Primary key is not allowed in a has_and_belongs_to_many join table (#{reflection.options[:join_table]}).") - end - end - class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc: def initialize(reflection) super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.") @@ -1589,19 +1583,16 @@ module ActiveRecord def create_has_and_belongs_to_many_reflection(association_id, options, &extension) options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association) + options[:extend] = create_extension_modules(association_id, extension, options[:extend]) reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self) - reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name)) - + if reflection.association_foreign_key == reflection.primary_key_name raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection) end - if connection.supports_primary_key? && - (connection.primary_key(reflection.options[:join_table]) rescue false) - raise HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection) - end + reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name)) reflection end @@ -1610,6 +1601,15 @@ module ActiveRecord [ associations ].flatten.collect { |association| reflect_on_association(association.to_s.intern) } end + def guard_against_unlimitable_reflections(reflections, options) + if (options[:offset] || options[:limit]) && !using_limitable_reflections?(reflections) + raise( + ConfigurationError, + "You can not use offset and limit together with has_many or has_and_belongs_to_many associations" + ) + end + end + def select_all_rows(options, join_dependency) connection.select_all( construct_finder_sql_with_included_associations(options, join_dependency), diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index af9ce3d..cb60baa 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -1,6 +1,11 @@ module ActiveRecord module Associations class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc: + def initialize(owner, reflection) + super + @primary_key_list = {} + end + def create(attributes = {}) create_record(attributes) { |record| insert_record(record) } end @@ -17,6 +22,12 @@ module ActiveRecord @reflection.reset_column_information end + def has_primary_key? + return @has_primary_key unless @has_primary_key.nil? + @has_primary_key = (ActiveRecord::Base.connection.supports_primary_key? && + ActiveRecord::Base.connection.primary_key(@reflection.options[:join_table])) + end + protected def construct_find_options!(options) options[:joins] = @join_sql @@ -29,6 +40,11 @@ module ActiveRecord end def insert_record(record, force = true, validate = true) + if has_primary_key? + raise ActiveRecord::ConfigurationError, + "Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})." + end + if record.new_record? if force record.save! diff --git a/activerecord/test/cases/associations/habtm_join_table_test.rb b/activerecord/test/cases/associations/habtm_join_table_test.rb index 745f169..bf3e04c 100644 --- a/activerecord/test/cases/associations/habtm_join_table_test.rb +++ b/activerecord/test/cases/associations/habtm_join_table_test.rb @@ -36,9 +36,21 @@ class HabtmJoinTableTest < ActiveRecord::TestCase uses_transaction :test_should_raise_exception_when_join_table_has_a_primary_key def test_should_raise_exception_when_join_table_has_a_primary_key if ActiveRecord::Base.connection.supports_primary_key? - assert_raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError do - MyReader.has_and_belongs_to_many :my_books + assert_raise ActiveRecord::ConfigurationError do + jaime = MyReader.create(:name=>"Jaime") + jaime.my_books << MyBook.create(:name=>'Great Expectations') end end end + + uses_transaction :test_should_cache_result_of_primary_key_check + def test_should_cache_result_of_primary_key_check + if ActiveRecord::Base.connection.supports_primary_key? + ActiveRecord::Base.connection.stubs(:primary_key).with('my_books_my_readers').returns(false).once + weaz = MyReader.create(:name=>'Weaz') + + weaz.my_books << MyBook.create(:name=>'Great Expectations') + weaz.my_books << MyBook.create(:name=>'Greater Expectations') + end + end end diff --git a/activerecord/test/fixtures/edges.yml b/activerecord/test/fixtures/edges.yml index b804f7b..c16c70d 100644 --- a/activerecord/test/fixtures/edges.yml +++ b/activerecord/test/fixtures/edges.yml @@ -1,5 +1,6 @@ <% (1..4).each do |id| %> edge_<%= id %>: + id: <%= id %> source_id: <%= id %> sink_id: <%= id + 1 %> -<% end %> +<% end %> \ No newline at end of file diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index da71aac..87fad0e 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -153,7 +153,7 @@ ActiveRecord::Schema.define do t.integer :access_level, :default => 1 end - create_table :edges, :force => true, :id => false do |t| + create_table :edges, :force => true do |t| t.column :source_id, :integer, :null => false t.column :sink_id, :integer, :null => false end -- 1.6.4.2 From 1f07a89c5946910fc28ea5ccd1da6af8a0f972a0 Mon Sep 17 00:00:00 2001 From: Coda Hale Date: Thu, 13 Aug 2009 10:03:08 -0700 Subject: [PATCH 278/779] Fix timing attack vulnerability in ActiveSupport::MessageVerifier. Use a constant-time comparison algorithm to compare the candidate HMAC with the calculated HMAC to prevent leaking information about the calculated HMAC. Signed-off-by: Michael Koziarski --- .../lib/active_support/message_verifier.rb | 19 ++++++++++++++++--- 1 files changed, 16 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index b24acb9..aae5a34 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -25,10 +25,10 @@ module ActiveSupport def verify(signed_message) data, digest = signed_message.split("--") - if digest != generate_digest(data) - raise InvalidSignature - else + if secure_compare(digest, generate_digest(data)) Marshal.load(ActiveSupport::Base64.decode64(data)) + else + raise InvalidSignature end end @@ -38,6 +38,19 @@ module ActiveSupport end private + # constant-time comparison algorithm to prevent timing attacks + def secure_compare(a, b) + if a.length == b.length + result = 0 + for i in 0..(a.length - 1) + result |= a[i] ^ b[i] + end + result == 0 + else + false + end + end + def generate_digest(data) require 'openssl' unless defined?(OpenSSL) OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@digest), @secret, data) -- 1.6.4.2 From 07c69380cf934d7173d116db87e8d8dbb9edd2b2 Mon Sep 17 00:00:00 2001 From: Manfred Stienstra Date: Wed, 26 Aug 2009 22:38:10 +0200 Subject: [PATCH 279/779] Add verify and clean methods to ActiveSupport::Multibyte. When accepting character input from outside of your application you can't blindly trust that all strings are properly encoded. With these methods you can check incoming strings and clean them up if necessary. Signed-off-by: Michael Koziarski --- activesupport/lib/active_support/multibyte.rb | 36 ++++- .../lib/active_support/multibyte/chars.rb | 23 +--- .../lib/active_support/multibyte/utils.rb | 61 +++++++++ activesupport/test/multibyte_utils_test.rb | 141 ++++++++++++++++++++ 4 files changed, 239 insertions(+), 22 deletions(-) create mode 100644 activesupport/lib/active_support/multibyte/utils.rb create mode 100644 activesupport/test/multibyte_utils_test.rb diff --git a/activesupport/lib/active_support/multibyte.rb b/activesupport/lib/active_support/multibyte.rb index 65a96af..b6354ee 100644 --- a/activesupport/lib/active_support/multibyte.rb +++ b/activesupport/lib/active_support/multibyte.rb @@ -1,9 +1,5 @@ # encoding: utf-8 -require 'active_support/multibyte/chars' -require 'active_support/multibyte/exceptions' -require 'active_support/multibyte/unicode_database' - module ActiveSupport #:nodoc: module Multibyte # A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more @@ -27,7 +23,35 @@ module ActiveSupport #:nodoc: # # Example: # ActiveSupport::Multibyte.proxy_class = CharsForUTF32 - mattr_accessor :proxy_class - self.proxy_class = ActiveSupport::Multibyte::Chars + def self.proxy_class=(klass) + @proxy_class = klass + end + + # Returns the currect proxy class + def self.proxy_class + @proxy_class ||= ActiveSupport::Multibyte::Chars + end + + # Regular expressions that describe valid byte sequences for a character + VALID_CHARACTER = { + # Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site) + 'UTF-8' => /\A(?: + [\x00-\x7f] | + [\xc2-\xdf] [\x80-\xbf] | + \xe0 [\xa0-\xbf] [\x80-\xbf] | + [\xe1-\xef] [\x80-\xbf] [\x80-\xbf] | + \xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] | + [\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] | + \xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf])\z /xn, + # Quick check for valid Shift-JIS characters, disregards the odd-even pairing + 'Shift_JIS' => /\A(?: + [\x00-\x7e \xa1-\xdf] | + [\x81-\x9f \xe0-\xef] [\x40-\x7e \x80-\x9e \x9f-\xfc])\z /xn + } end end + +require 'active_support/multibyte/chars' +require 'active_support/multibyte/exceptions' +require 'active_support/multibyte/unicode_database' +require 'active_support/multibyte/utils' diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 3d392d2..16bc130 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -73,16 +73,7 @@ module ActiveSupport #:nodoc: UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/ UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/ - # Borrowed from the Kconv library by Shinji KONO - (also as seen on the W3C site) - UTF8_PAT = /\A(?: - [\x00-\x7f] | - [\xc2-\xdf] [\x80-\xbf] | - \xe0 [\xa0-\xbf] [\x80-\xbf] | - [\xe1-\xef] [\x80-\xbf] [\x80-\xbf] | - \xf0 [\x90-\xbf] [\x80-\xbf] [\x80-\xbf] | - [\xf1-\xf3] [\x80-\xbf] [\x80-\xbf] [\x80-\xbf] | - \xf4 [\x80-\x8f] [\x80-\xbf] [\x80-\xbf] - )*\z/xn + UTF8_PAT = ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'] attr_reader :wrapped_string alias to_s wrapped_string @@ -307,23 +298,23 @@ module ActiveSupport #:nodoc: def rstrip chars(@wrapped_string.gsub(UNICODE_TRAILERS_PAT, '')) end - + # Strips entire range of Unicode whitespace from the left of the string. def lstrip chars(@wrapped_string.gsub(UNICODE_LEADERS_PAT, '')) end - + # Strips entire range of Unicode whitespace from the right and left of the string. def strip rstrip.lstrip end - + # Returns the number of codepoints in the string def size self.class.u_unpack(@wrapped_string).size end alias_method :length, :size - + # Reverses all characters in the string. # # Example: @@ -331,7 +322,7 @@ module ActiveSupport #:nodoc: def reverse chars(self.class.u_unpack(@wrapped_string).reverse.pack('U*')) end - + # Implements Unicode-aware slice with codepoints. Slicing on one point returns the codepoints for that # character. # @@ -646,7 +637,7 @@ module ActiveSupport #:nodoc: string.split(//u).map do |c| c.force_encoding(Encoding::ASCII) if c.respond_to?(:force_encoding) - if !UTF8_PAT.match(c) + if !ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'].match(c) n = c.unpack('C')[0] n < 128 ? n.chr : n < 160 ? [UCD.cp1252[n] || n].pack('U') : diff --git a/activesupport/lib/active_support/multibyte/utils.rb b/activesupport/lib/active_support/multibyte/utils.rb new file mode 100644 index 0000000..acef84d --- /dev/null +++ b/activesupport/lib/active_support/multibyte/utils.rb @@ -0,0 +1,61 @@ +# encoding: utf-8 + +module ActiveSupport #:nodoc: + module Multibyte #:nodoc: + if Kernel.const_defined?(:Encoding) + # Returns a regular expression that matches valid characters in the current encoding + def self.valid_character + VALID_CHARACTER[Encoding.default_internal.to_s] + end + else + def self.valid_character + case $KCODE + when 'UTF8' + VALID_CHARACTER['UTF-8'] + when 'SJIS' + VALID_CHARACTER['Shift_JIS'] + end + end + end + + if 'string'.respond_to?(:valid_encoding?) + # Verifies the encoding of a string + def self.verify(string) + string.valid_encoding? + end + else + def self.verify(string) + if expression = valid_character + for c in string.split(//) + return false unless valid_character.match(c) + end + end + true + end + end + + # Verifies the encoding of the string and raises an exception when it's not valid + def self.verify!(string) + raise EncodingError.new("Found characters with invalid encoding") unless verify(string) + end + + if 'string'.respond_to?(:force_encoding) + # Removes all invalid characters from the string. + # + # Note: this method is a no-op in Ruby 1.9 + def self.clean(string) + string + end + else + def self.clean(string) + if expression = valid_character + stripped = []; for c in string.split(//) + stripped << c if valid_character.match(c) + end; stripped.join + else + string + end + end + end + end +end \ No newline at end of file diff --git a/activesupport/test/multibyte_utils_test.rb b/activesupport/test/multibyte_utils_test.rb new file mode 100644 index 0000000..d8ac5ff --- /dev/null +++ b/activesupport/test/multibyte_utils_test.rb @@ -0,0 +1,141 @@ +# encoding: utf-8 + +require 'abstract_unit' +require 'multibyte_test_helpers' + +class MultibyteUtilsTest < ActiveSupport::TestCase + include MultibyteTestHelpers + + test "valid_character returns an expression for the current encoding" do + with_encoding('None') do + assert_nil ActiveSupport::Multibyte.valid_character + end + with_encoding('UTF8') do + assert_equal ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'], ActiveSupport::Multibyte.valid_character + end + with_encoding('SJIS') do + assert_equal ActiveSupport::Multibyte::VALID_CHARACTER['Shift_JIS'], ActiveSupport::Multibyte.valid_character + end + end + + test "verify verifies ASCII strings are properly encoded" do + with_encoding('None') do + examples.each do |example| + assert ActiveSupport::Multibyte.verify(example) + end + end + end + + test "verify verifies UTF-8 strings are properly encoded" do + with_encoding('UTF8') do + assert ActiveSupport::Multibyte.verify(example('valid UTF-8')) + assert !ActiveSupport::Multibyte.verify(example('invalid UTF-8')) + end + end + + test "verify verifies Shift-JIS strings are properly encoded" do + with_encoding('SJIS') do + assert ActiveSupport::Multibyte.verify(example('valid Shift-JIS')) + assert !ActiveSupport::Multibyte.verify(example('invalid Shift-JIS')) + end + end + + test "verify! raises an exception when it finds an invalid character" do + with_encoding('UTF8') do + assert_raises(ActiveSupport::Multibyte::EncodingError) do + ActiveSupport::Multibyte.verify!(example('invalid UTF-8')) + end + end + end + + test "verify! doesn't raise an exception when the encoding is valid" do + with_encoding('UTF8') do + assert_nothing_raised do + ActiveSupport::Multibyte.verify!(example('valid UTF-8')) + end + end + end + + if RUBY_VERSION < '1.9' + test "clean leaves ASCII strings intact" do + with_encoding('None') do + [ + 'word', "\270\236\010\210\245" + ].each do |string| + assert_equal string, ActiveSupport::Multibyte.clean(string) + end + end + end + + test "clean cleans invalid characters from UTF-8 encoded strings" do + with_encoding('UTF8') do + cleaned_utf8 = [8].pack('C*') + assert_equal example('valid UTF-8'), ActiveSupport::Multibyte.clean(example('valid UTF-8')) + assert_equal cleaned_utf8, ActiveSupport::Multibyte.clean(example('invalid UTF-8')) + end + end + + test "clean cleans invalid characters from Shift-JIS encoded strings" do + with_encoding('SJIS') do + cleaned_sjis = [184, 0, 136, 165].pack('C*') + assert_equal example('valid Shift-JIS'), ActiveSupport::Multibyte.clean(example('valid Shift-JIS')) + assert_equal cleaned_sjis, ActiveSupport::Multibyte.clean(example('invalid Shift-JIS')) + end + end + else + test "clean is a no-op" do + with_encoding('UTF8') do + assert_equal example('invalid Shift-JIS'), ActiveSupport::Multibyte.clean(example('invalid Shift-JIS')) + end + end + end + + private + + STRINGS = { + 'valid ASCII' => [65, 83, 67, 73, 73].pack('C*'), + 'invalid ASCII' => [128].pack('C*'), + 'valid UTF-8' => [227, 129, 147, 227, 129, 171, 227, 129, 161, 227, 130, 143].pack('C*'), + 'invalid UTF-8' => [184, 158, 8, 136, 165].pack('C*'), + 'valid Shift-JIS' => [131, 122, 129, 91, 131, 128].pack('C*'), + 'invalid Shift-JIS' => [184, 158, 8, 0, 255, 136, 165].pack('C*') + } + + if Kernel.const_defined?(:Encoding) + def example(key) + STRINGS[key].force_encoding(Encoding.default_internal) + end + + def examples + STRINGS.values.map { |s| s.force_encoding(Encoding.default_internal) } + end + else + def example(key) + STRINGS[key] + end + + def examples + STRINGS.values + end + end + + if 'string'.respond_to?(:encoding) + def with_encoding(enc) + before = Encoding.default_internal + + case enc + when 'UTF8' + Encoding.default_internal = Encoding::UTF_8 + when 'SJIS' + Encoding.default_internal = Encoding::Shift_JIS + else + Encoding.default_internal = Encoding::BINARY + end + yield + + Encoding.default_internal = before + end + else + alias with_encoding with_kcode + end +end \ No newline at end of file -- 1.6.4.2 From 9a68c72b4bd9073d6405f69791d9348ab26d8415 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Mon, 31 Aug 2009 12:07:30 -0700 Subject: [PATCH 280/779] Clean tag attributes before passing through the escape_once logic. Addresses CVE-2009-3009 --- actionpack/lib/action_view/helpers/tag_helper.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index af8c4d5..db99a0e 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -103,7 +103,7 @@ module ActionView # escape_once("<< Accept & Checkout") # # => "<< Accept & Checkout" def escape_once(html) - html.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] } + ActiveSupport::Multibyte.clean(html.to_s).gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] } end private -- 1.6.4.2 From a43ef2436cc6b63af35636127e42495ceb538cee Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Fri, 4 Sep 2009 09:56:09 +1200 Subject: [PATCH 281/779] Prepare for 2.3.4 release --- actionmailer/CHANGELOG | 4 ++++ actionmailer/Rakefile | 2 +- actionmailer/lib/action_mailer/version.rb | 2 +- actionpack/CHANGELOG | 4 +++- actionpack/Rakefile | 2 +- actionpack/lib/action_pack/version.rb | 2 +- activerecord/CHANGELOG | 2 +- activerecord/Rakefile | 2 +- activerecord/lib/active_record/version.rb | 2 +- activeresource/CHANGELOG | 2 +- activeresource/Rakefile | 2 +- activeresource/lib/active_resource/version.rb | 2 +- activesupport/CHANGELOG | 6 ++++++ activesupport/lib/active_support/version.rb | 2 +- railties/CHANGELOG | 2 +- railties/Rakefile | 10 +++++----- railties/lib/rails/version.rb | 2 +- 17 files changed, 31 insertions(+), 19 deletions(-) diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 4090dd8..22f3022 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,3 +1,7 @@ +*2.3.4 (September 4, 2009)* + +* Minor bug fixes. + *2.3.3 (July 12, 2009)* * No changes, just a version bump. diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index e6dc259..6dcb7f4 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -54,7 +54,7 @@ spec = Gem::Specification.new do |s| s.rubyforge_project = "actionmailer" s.homepage = "http://www.rubyonrails.org" - s.add_dependency('actionpack', '= 2.3.3' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.4' + PKG_BUILD) s.has_rdoc = true s.requirements << 'none' diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index db86a29..855b0ef 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -2,7 +2,7 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 3 + TINY = 4 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index f007672..94894c0 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,4 +1,6 @@ -*2.3.4 (pending)* +*2.3.4 (September 4, 2009)* + +* Sanitize multibyte strings before escaping them with escape_once. CVE-2009-3009 * Introduce grouped_collection_select helper. #1249 [Dan Codeape, Erik Ostrom] diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 1182958..ade3f22 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -79,7 +79,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.4' + PKG_BUILD) s.add_dependency('rack', '~> 1.0.0') s.require_path = 'lib' diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 66fca7d..698330a 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -2,7 +2,7 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 3 + TINY = 4 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index bc53d5d..12618e2 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,4 @@ -*2.3.4 (pending)* +*2.3.4 (September 4, 2009)* * PostgreSQL: XML datatype support. #1874 [Leonardo Borges] diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 36e4303..71b2b51 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -192,7 +192,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.4' + PKG_BUILD) s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite" s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite" diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index cbd6958..7d75ee6 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,7 +2,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 3 + TINY = 4 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index bb38073..dde2d76 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,4 +1,4 @@ -*2.3.4 (pending)* +*2.3.4 (September 4, 2009)* * Add support for errors in JSON format. #1956 [Fabien Jakimowicz] diff --git a/activeresource/Rakefile b/activeresource/Rakefile index c6591cc..a8decd9 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -66,7 +66,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.4' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'active_resource' diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index 8c68de7..6499f32 100644 --- a/activeresource/lib/active_resource/version.rb +++ b/activeresource/lib/active_resource/version.rb @@ -2,7 +2,7 @@ module ActiveResource module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 3 + TINY = 4 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index cb57790..246f9b8 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,3 +1,9 @@ +*2.3.4 (September 4, 2009)* + +* Introduce ActiveSupport::Multibyte.clean to clean invalid multibyte strings. + +* Bug fixes + *2.3.3 (July 12, 2009)* * JSON: +Object#to_json+ calls +as_json+ to coerce itself into something natively encodable like +Hash+, +Integer+, or +String+. Override +as_json+ instead of +to_json+ so you're JSON-library-agnostic. [Jeremy Kemper] diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 24ce690..5d29336 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -2,7 +2,7 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 3 + TINY = 4 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 01ba2b9..fc28afe 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,4 +1,4 @@ -*2.3.4 (pending)* +*2.3.4 (September 4, 2009)* * I18n support for plugins. #2325 [Antonio Tapiador, Sven Fuchs] diff --git a/railties/Rakefile b/railties/Rakefile index 5a3d65c..b7c199f 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -314,11 +314,11 @@ spec = Gem::Specification.new do |s| EOF s.add_dependency('rake', '>= 0.8.3') - s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD) - s.add_dependency('activerecord', '= 2.3.3' + PKG_BUILD) - s.add_dependency('actionpack', '= 2.3.3' + PKG_BUILD) - s.add_dependency('actionmailer', '= 2.3.3' + PKG_BUILD) - s.add_dependency('activeresource', '= 2.3.3' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.4' + PKG_BUILD) + s.add_dependency('activerecord', '= 2.3.4' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.4' + PKG_BUILD) + s.add_dependency('actionmailer', '= 2.3.4' + PKG_BUILD) + s.add_dependency('activeresource', '= 2.3.4' + PKG_BUILD) s.rdoc_options << '--exclude' << '.' s.has_rdoc = false diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index d3f81d8..47171d1 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -2,7 +2,7 @@ module Rails module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 3 + TINY = 4 STRING = [MAJOR, MINOR, TINY].join('.') end -- 1.6.4.2 From 8a2cfe9de4e8b21fd5cecf21960f64a5c8a5a2a4 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 8 Sep 2009 14:05:33 +0900 Subject: [PATCH 282/779] Ruby 1.9: fix MessageVerifier#secure_compare --- .../lib/active_support/message_verifier.rb | 36 +++++++++++++++----- 1 files changed, 27 insertions(+), 9 deletions(-) diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index aae5a34..8d14423 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -38,16 +38,34 @@ module ActiveSupport end private - # constant-time comparison algorithm to prevent timing attacks - def secure_compare(a, b) - if a.length == b.length - result = 0 - for i in 0..(a.length - 1) - result |= a[i] ^ b[i] + if "foo".respond_to?(:force_encoding) + # constant-time comparison algorithm to prevent timing attacks + def secure_compare(a, b) + a = a.force_encoding(Encoding::BINARY) + b = b.force_encoding(Encoding::BINARY) + + if a.length == b.length + result = 0 + for i in 0..(a.length - 1) + result |= a[i].ord ^ b[i].ord + end + result == 0 + else + false + end + end + else + # For 1.8 + def secure_compare(a, b) + if a.length == b.length + result = 0 + for i in 0..(a.length - 1) + result |= a[i] ^ b[i] + end + result == 0 + else + false end - result == 0 - else - false end end -- 1.6.4.2 From e1b109633cb03ad84b88e8d1f9d3fcaef08619b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 8 Sep 2009 10:38:45 -0500 Subject: [PATCH 283/779] Allow scope to be changed for ActiveRecord::Errors#generate_full_message and change deprecation message [#1687 state:resolved] Signed-off-by: Joshua Peek --- activerecord/lib/active_record/validations.rb | 35 ++++++++++++---------- activerecord/test/cases/validations_i18n_test.rb | 8 +++++ 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index b339f0c..1dc7c96 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -27,11 +27,11 @@ module ActiveRecord end def message - generate_message(@message, options.dup) + generate_message(@message, default_options) end def full_message - attribute.to_s == 'base' ? message : generate_full_message(message, options.dup) + attribute.to_s == 'base' ? message : generate_full_message(message, default_options) end alias :to_s :message @@ -72,12 +72,7 @@ module ActiveRecord keys << @type unless @type == message keys.compact! - options.reverse_merge! :default => keys, - :scope => [:activerecord, :errors], - :model => @base.class.human_name, - :attribute => @base.class.human_attribute_name(attribute.to_s), - :value => value - + options.merge!(:default => keys) I18n.translate(keys.shift, options) end @@ -109,15 +104,23 @@ module ActiveRecord # title: # blank: This title is screwed! def generate_full_message(message, options = {}) - options.reverse_merge! :message => self.message, - :model => @base.class.human_name, - :attribute => @base.class.human_attribute_name(attribute.to_s), - :value => value + keys = [ + :"full_messages.#{@message}", + :'full_messages.format', + '{{attribute}} {{message}}' + ] - key = :"full_messages.#{@message}" - defaults = [:'full_messages.format', '{{attribute}} {{message}}'] + options.merge!(:default => keys, :message => self.message) + I18n.translate(keys.shift, options) + end - I18n.t(key, options.merge(:default => defaults, :scope => [:activerecord, :errors])) + # Return user options with default options. + # + def default_options + options.reverse_merge :scope => [:activerecord, :errors], + :model => @base.class.human_name, + :attribute => @base.class.human_attribute_name(attribute.to_s), + :value => value end end @@ -321,7 +324,7 @@ module ActiveRecord end def generate_message(attribute, message = :invalid, options = {}) - ActiveSupport::Deprecation.warn("ActiveRecord::Errors#generate_message has been deprecated. Please use ActiveRecord::Error#generate_message.") + ActiveSupport::Deprecation.warn("ActiveRecord::Errors#generate_message has been deprecated. Please use ActiveRecord::Error.new().to_s.") Error.new(@base, attribute, message, options).to_s end end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 36eadd5..f2e3e5a 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -651,6 +651,14 @@ class ActiveRecordErrorI18nTests < ActiveSupport::TestCase assert_full_message 'Title is kaputt?!', :title, :kaputt, :message => :broken end + test "#full_message with different scope" do + store_translations(:my_errors => { :messages => { :kaputt => 'is kaputt' } }) + assert_full_message 'Title is kaputt', :title, :kaputt, :scope => [:activerecord, :my_errors] + + store_translations(:my_errors => { :full_messages => { :kaputt => '{{attribute}} {{message}}!' } }) + assert_full_message 'Title is kaputt!', :title, :kaputt, :scope => [:activerecord, :my_errors] + end + # switch locales test "#message allows to switch locales" do -- 1.6.4.2 From a32eeebdcbf56f433e5a7410c0f235812c9fee00 Mon Sep 17 00:00:00 2001 From: Beau Harrington Date: Wed, 9 Sep 2009 22:25:23 -0700 Subject: [PATCH 284/779] Remove redundant checks for valid character regexp in ActiveSupport::Multibyte#clean and #verify. [#3181 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/active_support/multibyte/utils.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/multibyte/utils.rb b/activesupport/lib/active_support/multibyte/utils.rb index acef84d..a7e4068 100644 --- a/activesupport/lib/active_support/multibyte/utils.rb +++ b/activesupport/lib/active_support/multibyte/utils.rb @@ -27,7 +27,7 @@ module ActiveSupport #:nodoc: def self.verify(string) if expression = valid_character for c in string.split(//) - return false unless valid_character.match(c) + return false unless expression.match(c) end end true @@ -50,7 +50,7 @@ module ActiveSupport #:nodoc: def self.clean(string) if expression = valid_character stripped = []; for c in string.split(//) - stripped << c if valid_character.match(c) + stripped << c if expression.match(c) end; stripped.join else string -- 1.6.4.2 From ab9efe9e165ea1e8a8dc37287deea4439d3fbc05 Mon Sep 17 00:00:00 2001 From: Nathaniel Talbott Date: Thu, 10 Sep 2009 21:15:18 -0400 Subject: [PATCH 285/779] Fix filtering parameters when there are Fixnum or other un-dupable values. [#3184 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/base.rb | 2 +- actionpack/test/controller/filter_params_test.rb | 1 + 2 files changed, 2 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 1d8cc14..d66e666 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -502,7 +502,7 @@ module ActionController #:nodoc: end elsif block_given? key = key.dup - value = value.dup if value + value = value.dup if value.duplicable? yield key, value filtered_parameters[key] = value else diff --git a/actionpack/test/controller/filter_params_test.rb b/actionpack/test/controller/filter_params_test.rb index 6994aae..670edc1 100644 --- a/actionpack/test/controller/filter_params_test.rb +++ b/actionpack/test/controller/filter_params_test.rb @@ -18,6 +18,7 @@ class FilterParamTest < Test::Unit::TestCase test_hashes = [[{},{},[]], [{'foo'=>nil},{'foo'=>nil},[]], [{'foo'=>'bar'},{'foo'=>'bar'},[]], + [{'foo'=>1},{'foo'=>1},[]], [{'foo'=>'bar'},{'foo'=>'bar'},%w'food'], [{'foo'=>'bar'},{'foo'=>'[FILTERED]'},%w'foo'], [{'foo'=>'bar', 'bar'=>'foo'},{'foo'=>'[FILTERED]', 'bar'=>'foo'},%w'foo baz'], -- 1.6.4.2 From d2cf33e903bdbeff6e7e696fd20b23b0e6f3a077 Mon Sep 17 00:00:00 2001 From: Shugo Maeda Date: Fri, 11 Sep 2009 13:42:46 +0900 Subject: [PATCH 286/779] Removed the copyright notice not to show it in the result of 'ri ActiveRecord'. --- .../lib/active_record/locking/pessimistic.rb | 22 -------------------- 1 files changed, 0 insertions(+), 22 deletions(-) diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb index 3206595..fcc9ebb 100644 --- a/activerecord/lib/active_record/locking/pessimistic.rb +++ b/activerecord/lib/active_record/locking/pessimistic.rb @@ -1,25 +1,3 @@ -# Copyright (c) 2006 Shugo Maeda -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject -# to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR -# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - module ActiveRecord module Locking # Locking::Pessimistic provides support for row-level locking using -- 1.6.4.2 From 5de75398c495f109772b622291362a98bc6c21d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ku=C5=BAma?= Date: Fri, 11 Sep 2009 09:13:14 +0200 Subject: [PATCH 287/779] ruby 1.9 friendly secure_compare Signed-off-by: Michael Koziarski --- .../lib/active_support/message_verifier.rb | 15 ++++++--------- 1 files changed, 6 insertions(+), 9 deletions(-) diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index 8d14423..5596784 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -38,24 +38,21 @@ module ActiveSupport end private - if "foo".respond_to?(:force_encoding) + if "foo".respond_to?(:bytesize) # constant-time comparison algorithm to prevent timing attacks + # > 1.8.6 friendly version def secure_compare(a, b) - a = a.force_encoding(Encoding::BINARY) - b = b.force_encoding(Encoding::BINARY) - - if a.length == b.length + if a.bytesize == b.bytesize result = 0 - for i in 0..(a.length - 1) - result |= a[i].ord ^ b[i].ord - end + j = b.each_byte + a.each_byte { |i| result |= i ^ j.next } result == 0 else false end end else - # For 1.8 + # For <= 1.8.6 def secure_compare(a, b) if a.length == b.length result = 0 -- 1.6.4.2 From 028d449fe9d727650ca47318d5e8615dfc7bc7a8 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 27 Aug 2009 20:53:39 +0900 Subject: [PATCH 288/779] 1.9 compat: let -g option work with Ruby 1.9 [#3105 state:committed milestone:2.3.5] Signed-off-by: Jeremy Kemper --- railties/lib/rails_generator/options.rb | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/railties/lib/rails_generator/options.rb b/railties/lib/rails_generator/options.rb index 5f6aefa..c052891 100644 --- a/railties/lib/rails_generator/options.rb +++ b/railties/lib/rails_generator/options.rb @@ -137,10 +137,10 @@ module Rails end end opt.on('-g', '--git', 'Modify files with git. (Note: git must be in path)') do - options[:git] = `git status`.inject({:new => {}, :modified => {}}) do |opt, e| - opt[:new][e.chomp[14..-1]] = true if e =~ /new file:/ - opt[:modified][e.chomp[14..-1]] = true if e =~ /modified:/ - opt + options[:git] = {:new => {}, :modified => {}} + `git status`.each_line do |line| + options[:git][:new][line.chomp[14..-1]] = true if line =~ /new file:/ + options[:git][:modified][line.chomp[14..-1]] = true if line =~ /modified:/ end end end -- 1.6.4.2 From 3413643e83c09533c95a1ccf0d9785f6d892203c Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Fri, 28 Aug 2009 11:46:36 +0900 Subject: [PATCH 289/779] 1.9 compat: let -c option work with Ruby 1.9 [#3109 state:committed milestone:2.3.5] Signed-off-by: Jeremy Kemper --- railties/lib/rails_generator/options.rb | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails_generator/options.rb b/railties/lib/rails_generator/options.rb index c052891..070f96b 100644 --- a/railties/lib/rails_generator/options.rb +++ b/railties/lib/rails_generator/options.rb @@ -131,9 +131,9 @@ module Rails opt.on('-q', '--quiet', 'Suppress normal output.') { |v| options[:quiet] = v } opt.on('-t', '--backtrace', 'Debugging: show backtrace on errors.') { |v| options[:backtrace] = v } opt.on('-c', '--svn', 'Modify files with subversion. (Note: svn must be in path)') do - options[:svn] = `svn status`.inject({}) do |opt, e| - opt[e.chomp[7..-1]] = true - opt + options[:svn] = {} + `svn status`.each_line do |line| + options[:svn][line.chomp[7..-1]] = true end end opt.on('-g', '--git', 'Modify files with git. (Note: git must be in path)') do -- 1.6.4.2 From 596406f90a8037a08d59ce31cddfc16b5f12b6a1 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 19 Mar 2009 14:40:42 +0900 Subject: [PATCH 290/779] Fix default_error_messages back to the original message Signed-off-by: Jeremy Kemper --- activerecord/test/cases/validations_test.rb | 10 +++++++--- 1 files changed, 7 insertions(+), 3 deletions(-) diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 4c023c3..7d88758 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -905,14 +905,18 @@ class ValidationsTest < ActiveRecord::TestCase end def test_validates_length_with_globally_modified_error_message - ActiveSupport::Deprecation.silence do - ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}' - end + defaults = ActiveSupport::Deprecation.silence { ActiveRecord::Errors.default_error_messages } + original_message = defaults[:too_short] + defaults[:too_short] = 'tu est trops petit hombre {{count}}' + Topic.validates_length_of :title, :minimum => 10 t = Topic.create(:title => 'too short') assert !t.valid? assert_equal 'tu est trops petit hombre 10', t.errors['title'] + + ensure + defaults[:too_short] = original_message end def test_validates_size_of_association -- 1.6.4.2 From 44fbc86ab8975d9e254c7793d54996d2a9ce3f94 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 19 Mar 2009 14:42:08 +0900 Subject: [PATCH 291/779] Ensure validation errors to be ordered in declared order [#2301 state:committed milestone:2.3.5] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/validations.rb | 5 +++-- activerecord/test/cases/validations_test.rb | 22 ++++++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 1dc7c96..a7c49a0 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -137,7 +137,8 @@ module ActiveRecord end def initialize(base) # :nodoc: - @base, @errors = base, {} + @base = base + clear end # Adds an error to the base object instead of any particular attribute. This is used @@ -286,7 +287,7 @@ module ActiveRecord # Removes all errors that have been added. def clear - @errors = {} + @errors = ActiveSupport::OrderedHash.new end # Returns the total number of errors added. Two errors added to the same attribute will be counted as such. diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 7d88758..5678da2 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -1436,12 +1436,22 @@ class ValidationsTest < ActiveRecord::TestCase end def test_validation_order - Topic.validates_presence_of :title - Topic.validates_length_of :title, :minimum => 2 - - t = Topic.new("title" => "") - assert !t.valid? - assert_equal "can't be blank", t.errors.on("title").first + Topic.validates_presence_of :title, :author_name + Topic.validate {|topic| topic.errors.add('author_email_address', 'will never be valid')} + Topic.validates_length_of :title, :content, :minimum => 2 + + t = Topic.new :title => '' + t.valid? + e = t.errors.instance_variable_get '@errors' + assert_equal 'title', key = e.keys.first + assert_equal "can't be blank", t.errors.on(key).first + assert_equal 'is too short (minimum is 2 characters)', t.errors.on(key).second + assert_equal 'author_name', key = e.keys.second + assert_equal "can't be blank", t.errors.on(key) + assert_equal 'author_email_address', key = e.keys.third + assert_equal 'will never be valid', t.errors.on(key) + assert_equal 'content', key = e.keys.fourth + assert_equal 'is too short (minimum is 2 characters)', t.errors.on(key) end def test_invalid_should_be_the_opposite_of_valid -- 1.6.4.2 From c5e3309bb4e63fafb363da8f7eb2364530f9f96f Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Fri, 20 Feb 2009 01:27:43 +0900 Subject: [PATCH 292/779] Ruby 1.9 compat: Avoid using the return value of FileUtils.mkdir_p, as it does not return a String but an Array in Ruby 1.9 [#2018 state:committed milestone:2.3.5] Signed-off-by: Jeremy Kemper --- railties/lib/commands/plugin.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/commands/plugin.rb b/railties/lib/commands/plugin.rb index 8589b16..9f99a8d 100644 --- a/railties/lib/commands/plugin.rb +++ b/railties/lib/commands/plugin.rb @@ -268,7 +268,7 @@ class Plugin def install_using_git(options = {}) root = rails_env.root - install_path = mkdir_p "#{root}/vendor/plugins/#{name}" + mkdir_p(install_path = "#{root}/vendor/plugins/#{name}") Dir.chdir install_path do init_cmd = "git init" init_cmd += " -q" if options[:quiet] and not $verbose -- 1.6.4.2 From 08d15f86c447fea31132d11df03ff5df41650f50 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 12 Sep 2009 02:45:33 -0700 Subject: [PATCH 293/779] Deprecate "Allow frameworks to be required by their gem name" This has just been confusing. Better to educate than band-aid. This deprecates commit 18a24274ec823ded4ffa29bf33fd3d76816aab7e. Originally from http://dev.rubyonrails.org/ticket/8845 [drnic] --- actionmailer/lib/actionmailer.rb | 1 + actionpack/lib/actionpack.rb | 1 + activerecord/lib/activerecord.rb | 1 + activeresource/lib/activeresource.rb | 1 + activesupport/lib/activesupport.rb | 1 + 5 files changed, 5 insertions(+), 0 deletions(-) diff --git a/actionmailer/lib/actionmailer.rb b/actionmailer/lib/actionmailer.rb index 5064162..878607e 100644 --- a/actionmailer/lib/actionmailer.rb +++ b/actionmailer/lib/actionmailer.rb @@ -1 +1,2 @@ require 'action_mailer' +ActiveSupport::Deprecation.warn 'require "actionmailer" is deprecated and will be removed in Rails 3. Use require "action_mailer" instead.' diff --git a/actionpack/lib/actionpack.rb b/actionpack/lib/actionpack.rb index 2fe2832..41681db 100644 --- a/actionpack/lib/actionpack.rb +++ b/actionpack/lib/actionpack.rb @@ -1 +1,2 @@ require 'action_pack' +ActiveSupport::Deprecation.warn 'require "actionpack" is deprecated and will be removed in Rails 3. Use require "action_pack" instead.' diff --git a/activerecord/lib/activerecord.rb b/activerecord/lib/activerecord.rb index cd62b2a..bb6a0db 100644 --- a/activerecord/lib/activerecord.rb +++ b/activerecord/lib/activerecord.rb @@ -1 +1,2 @@ require 'active_record' +ActiveSupport::Deprecation.warn 'require "activerecord" is deprecated and will be removed in Rails 3. Use require "active_record" instead.' diff --git a/activeresource/lib/activeresource.rb b/activeresource/lib/activeresource.rb index e076455..666656c 100644 --- a/activeresource/lib/activeresource.rb +++ b/activeresource/lib/activeresource.rb @@ -1 +1,2 @@ require 'active_resource' +ActiveSupport::Deprecation.warn 'require "activeresource" is deprecated and will be removed in Rails 3. Use require "active_resource" instead.' diff --git a/activesupport/lib/activesupport.rb b/activesupport/lib/activesupport.rb index 69f36f7..9cae13f 100644 --- a/activesupport/lib/activesupport.rb +++ b/activesupport/lib/activesupport.rb @@ -1 +1,2 @@ require 'active_support' +ActiveSupport::Deprecation.warn 'require "activesupport" is deprecated and will be removed in Rails 3. Use require "active_support" instead.' -- 1.6.4.2 From 3f59a73cb626863910da86bd79989760f5862d15 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 12 Sep 2009 02:47:25 -0700 Subject: [PATCH 294/779] activesupport -> active_support --- .../flush_cache_on_private_memoization_test.rb | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/activesupport/test/flush_cache_on_private_memoization_test.rb b/activesupport/test/flush_cache_on_private_memoization_test.rb index ddbd05b..1cd313e 100644 --- a/activesupport/test/flush_cache_on_private_memoization_test.rb +++ b/activesupport/test/flush_cache_on_private_memoization_test.rb @@ -1,5 +1,4 @@ -require 'rubygems' -require 'activesupport' +require 'active_support' require 'test/unit' class FlashCacheOnPrivateMemoizationTest < Test::Unit::TestCase @@ -41,4 +40,4 @@ class FlashCacheOnPrivateMemoizationTest < Test::Unit::TestCase end end -end \ No newline at end of file +end -- 1.6.4.2 From d48d3d0f41f51e5da3f27105cdcee8a4c3852ea1 Mon Sep 17 00:00:00 2001 From: sdsykes Date: Fri, 11 Sep 2009 12:53:57 +0300 Subject: [PATCH 295/779] Fix habtm associations when using multiple databases [#3128] Signed-off-by: Jeremy Kemper --- .../has_and_belongs_to_many_association.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index cb60baa..83a4a8b 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -24,8 +24,8 @@ module ActiveRecord def has_primary_key? return @has_primary_key unless @has_primary_key.nil? - @has_primary_key = (ActiveRecord::Base.connection.supports_primary_key? && - ActiveRecord::Base.connection.primary_key(@reflection.options[:join_table])) + @has_primary_key = (@owner.connection.supports_primary_key? && + @owner.connection.primary_key(@reflection.options[:join_table])) end protected -- 1.6.4.2 From 52a50db6c0183bf53c6760cce74529c13ed3e271 Mon Sep 17 00:00:00 2001 From: Mike Breen Date: Sat, 11 Jul 2009 14:30:35 +0200 Subject: [PATCH 296/779] Raise an exception with friendlier error message when attempting to build a polymorphic belongs_to with accepts_nested_attributes_for. [#2318 state:resolved] Signed-off-by: Eloy Duran --- .../lib/active_record/nested_attributes.rb | 7 ++++++- activerecord/test/cases/nested_attributes_test.rb | 8 ++++++++ activerecord/test/models/treasure.rb | 2 ++ 3 files changed, 16 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index e3122d1..9b4b954 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -247,7 +247,12 @@ module ActiveRecord if attributes['id'].blank? unless reject_new_record?(association_name, attributes) - send("build_#{association_name}", attributes.except(*UNASSIGNABLE_KEYS)) + method = "build_#{association_name}" + if respond_to?(method) + send(method, attributes.except(*UNASSIGNABLE_KEYS)) + else + raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?" + end end elsif (existing_record = send(association_name)) && existing_record.id.to_s == attributes['id'].to_s assign_to_or_mark_for_destruction(existing_record, attributes, allow_destroy) diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index cd6277c..7acdf43 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -62,11 +62,19 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase end class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase + include AssertRaiseWithMessage + def setup @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?") @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning') end + def test_should_raise_argument_error_if_trying_to_build_polymorphic_belongs_to + assert_raise_with_message ArgumentError, "Cannot build association looter. Are you trying to build a polymorphic one-to-one association?" do + Treasure.new(:name => 'pearl', :looter_attributes => {:catchphrase => "Arrr"}) + end + end + def test_should_define_an_attribute_writer_method_for_the_association assert_respond_to @pirate, :ship_attributes= end diff --git a/activerecord/test/models/treasure.rb b/activerecord/test/models/treasure.rb index 97c690c..2a98e74 100644 --- a/activerecord/test/models/treasure.rb +++ b/activerecord/test/models/treasure.rb @@ -3,4 +3,6 @@ class Treasure < ActiveRecord::Base belongs_to :looter, :polymorphic => true has_many :price_estimates, :as => :estimate_of + + accepts_nested_attributes_for :looter end -- 1.6.4.2 From 7c1e4ef64bd26927708da641584a6a13d9b1391c Mon Sep 17 00:00:00 2001 From: Lance Ivy Date: Sat, 11 Jul 2009 15:09:09 +0200 Subject: [PATCH 297/779] Don't cascade autosave validation to destroyed children. [#2761 state:resolved] Signed-off-by: Eloy Duran --- .../lib/active_record/autosave_association.rb | 2 ++ activerecord/lib/active_record/base.rb | 7 +++++++ .../test/cases/autosave_association_test.rb | 11 +++++++++++ activerecord/test/cases/base_test.rb | 2 ++ 4 files changed, 22 insertions(+), 0 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 1b73b0f..bd175b8 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -246,6 +246,8 @@ module ActiveRecord # the parent, self, if it wasn't. Skips any :autosave # enabled records if they're marked_for_destruction?. def association_valid?(reflection, association) + return true if association.destroyed? + unless valid = association.valid? if reflection.options[:autosave] unless association.marked_for_destruction? diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index c17702d..2ec2f73 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2567,6 +2567,7 @@ module ActiveRecord #:nodoc: # options, use #destroy. def delete self.class.delete(id) unless new_record? + @destroyed = true freeze end @@ -2581,6 +2582,7 @@ module ActiveRecord #:nodoc: ) end + @destroyed = true freeze end @@ -2840,6 +2842,11 @@ module ActiveRecord #:nodoc: @attributes.frozen? end + # Returns +true+ if the record has been destroyed. + def destroyed? + @destroyed + end + # Returns +true+ if the record is read only. Records loaded through joins with piggy-back # attributes will be marked as read only since they cannot be saved. def readonly? diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 919b6f8..0b20384 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -556,6 +556,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase children.each { |child| child.mark_for_destruction } assert_difference("#{association_name.classify}.count", -2) { @pirate.save! } end + + define_method("test_should_skip_validation_on_the_#{association_name}_association_if_destroyed") do + @pirate.send(association_name).create!(:name => "#{association_name}_1") + children = @pirate.send(association_name) + + children.each { |child| child.name = '' } + assert !@pirate.valid? + + children.each { |child| child.destroy } + assert @pirate.valid? + end define_method("test_should_rollback_destructions_if_an_exception_occurred_while_saving_#{association_name}") do 2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") } diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 4530eec..14f3967 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -474,6 +474,7 @@ class BasicsTest < ActiveRecord::TestCase topic = Topic.find(1) assert_equal topic, topic.delete, 'topic.delete did not return self' assert topic.frozen?, 'topic not frozen after delete' + assert topic.destroyed?, 'topic not marked as being destroyed' assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } end @@ -486,6 +487,7 @@ class BasicsTest < ActiveRecord::TestCase topic = Topic.find(1) assert_equal topic, topic.destroy, 'topic.destroy did not return self' assert topic.frozen?, 'topic not frozen after destroy' + assert topic.destroyed?, 'topic not marked as being destroyed' assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) } end -- 1.6.4.2 From 55bc0c76f8b9d19402ce51623d027feb03dced41 Mon Sep 17 00:00:00 2001 From: Andrew France Date: Sat, 11 Jul 2009 16:38:35 +0200 Subject: [PATCH 298/779] Allow fields_for on a nested_attributes association to accept an explicit collection to be used. [#2648 state:resolved] Signed-off-by: Eloy Duran --- actionpack/lib/action_view/helpers/form_helper.rb | 26 +++++++++++---- actionpack/test/template/form_helper_test.rb | 36 +++++++++++++++++++++ 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index db7de8e..0e097e6 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -445,6 +445,15 @@ module ActionView # <% end %> # <% end %> # + # Or a collection to be used: + # + # <% form_for @person, :url => { :action => "update" } do |person_form| %> + # ... + # <% person_form.fields_for :projects, @active_projects do |project_fields| %> + # Name: <%= project_fields.text_field :name %> + # <% end %> + # <% end %> + # # When projects is already an association on Person you can use # +accepts_nested_attributes_for+ to define the writer method for you: # @@ -1013,18 +1022,21 @@ module ActionView def fields_for_with_nested_attributes(association_name, args, block) name = "#{object_name}[#{association_name}_attributes]" - association = @object.send(association_name) - explicit_object = args.first if args.first.respond_to?(:new_record?) + association = args.first + + if association.respond_to?(:new_record?) + association = [association] if @object.send(association_name).is_a?(Array) + elsif !association.is_a?(Array) + association = @object.send(association_name) + end if association.is_a?(Array) - children = explicit_object ? [explicit_object] : association explicit_child_index = args.last[:child_index] if args.last.is_a?(Hash) - - children.map do |child| + association.map do |child| fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, args, block) end.join - else - fields_for_nested_model(name, explicit_object || association, args, block) + elsif association + fields_for_nested_model(name, association, args, block) end end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index a32cdbe..41e1583 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -772,6 +772,42 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_an_empty_supplied_attributes_collection + form_for(:post, @post) do |f| + concat f.text_field(:title) + f.fields_for(:comments, []) do |cf| + concat cf.text_field(:name) + end + end + + expected = '
' + + '' + + '
' + + assert_dom_equal expected, output_buffer + end + + def test_nested_fields_for_with_existing_records_on_a_supplied_nested_attributes_collection + @post.comments = Array.new(2) { |id| Comment.new(id + 1) } + + form_for(:post, @post) do |f| + concat f.text_field(:title) + f.fields_for(:comments, @post.comments) do |cf| + concat cf.text_field(:name) + end + end + + expected = '
' + + '' + + '' + + '' + + '' + + '' + + '
' + + assert_dom_equal expected, output_buffer + end + def test_nested_fields_for_on_a_nested_attributes_collection_association_yields_only_builder @post.comments = [Comment.new(321), Comment.new] yielded_comments = [] -- 1.6.4.2 From 2420d6272c8dfa1ebd6735fc9b29e73291b0b93c Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Sat, 11 Jul 2009 17:52:13 +0200 Subject: [PATCH 299/779] Explicitely setting `autosave => false' should override new_record autosaving. [#2214 state:resolved] Original author is Jacob. --- .../lib/active_record/autosave_association.rb | 6 +- .../test/cases/autosave_association_test.rb | 64 ++++++++++++++++++++ activerecord/test/cases/reflection_test.rb | 6 +- activerecord/test/models/company.rb | 5 ++ 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index bd175b8..73aa3d9 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -287,7 +287,7 @@ module ActiveRecord records.each do |record| if autosave && record.marked_for_destruction? association.destroy(record) - elsif @new_record_before_save || record.new_record? + elsif autosave != false && (@new_record_before_save || record.new_record?) if autosave association.send(:insert_record, record, false, false) else @@ -318,7 +318,7 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy - elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave + elsif autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave) association[reflection.primary_key_name] = id association.save(!autosave) end @@ -339,7 +339,7 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy - else + elsif autosave != false association.save(!autosave) if association.new_record? || autosave if association.updated? diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 0b20384..92e27e2 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -436,6 +436,70 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa end end +class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase + def test_autosave_new_record_on_belongs_to_can_be_disabled_per_relationship + new_account = Account.new("credit_limit" => 1000) + new_firm = Firm.new("name" => "some firm") + + assert new_firm.new_record? + new_account.firm = new_firm + new_account.save! + + assert !new_firm.new_record? + + new_account = Account.new("credit_limit" => 1000) + new_autosaved_firm = Firm.new("name" => "some firm") + + assert new_autosaved_firm.new_record? + new_account.unautosaved_firm = new_autosaved_firm + new_account.save! + + assert new_autosaved_firm.new_record? + end + + def test_autosave_new_record_on_has_one_can_be_disabled_per_relationship + firm = Firm.new("name" => "some firm") + account = Account.new("credit_limit" => 1000) + + assert account.new_record? + firm.account = account + firm.save! + + assert !account.new_record? + + firm = Firm.new("name" => "some firm") + account = Account.new("credit_limit" => 1000) + + firm.unautosaved_account = account + + assert account.new_record? + firm.unautosaved_account = account + firm.save! + + assert account.new_record? + end + + def test_autosave_new_record_on_has_many_can_be_disabled_per_relationship + firm = Firm.new("name" => "some firm") + account = Account.new("credit_limit" => 1000) + + assert account.new_record? + firm.accounts << account + + firm.save! + assert !account.new_record? + + firm = Firm.new("name" => "some firm") + account = Account.new("credit_limit" => 1000) + + assert account.new_record? + firm.unautosaved_accounts << account + + firm.save! + assert account.new_record? + end +end + class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase self.use_transactional_fixtures = false diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index a0107d2..b18b99d 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -170,9 +170,9 @@ class ReflectionTest < ActiveRecord::TestCase def test_reflection_of_all_associations # FIXME these assertions bust a lot - assert_equal 30, Firm.reflect_on_all_associations.size - assert_equal 23, Firm.reflect_on_all_associations(:has_many).size - assert_equal 7, Firm.reflect_on_all_associations(:has_one).size + assert_equal 33, Firm.reflect_on_all_associations.size + assert_equal 25, Firm.reflect_on_all_associations(:has_many).size + assert_equal 8, Firm.reflect_on_all_associations(:has_one).size assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index d4bbbeb..16e8484 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -73,6 +73,10 @@ class Firm < Company has_one :readonly_account, :foreign_key => "firm_id", :class_name => "Account", :readonly => true has_one :account_using_primary_key, :primary_key => "firm_id", :class_name => "Account" has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete + + has_one :unautosaved_account, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false + has_many :accounts + has_many :unautosaved_accounts, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false end class DependentFirm < Company @@ -136,6 +140,7 @@ end class Account < ActiveRecord::Base belongs_to :firm + belongs_to :unautosaved_firm, :foreign_key => "firm_id", :class_name => "Firm", :autosave => false def self.destroyed_account_ids @destroyed_account_ids ||= Hash.new { |h,k| h[k] = [] } -- 1.6.4.2 From c52a50ec51f7466411c032d676590be67fd6a956 Mon Sep 17 00:00:00 2001 From: Dmitry Polushkin Date: Sat, 11 Jul 2009 18:46:11 +0200 Subject: [PATCH 300/779] Fix autosave association to skip validation if it is marked for destruction. [#2064 state:resolved] Signed-off-by: Eloy Duran --- .../lib/active_record/autosave_association.rb | 14 ++++++-------- .../test/cases/autosave_association_test.rb | 7 ++++++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 73aa3d9..58293fc 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -244,18 +244,16 @@ module ActiveRecord # Returns whether or not the association is valid and applies any errors to # the parent, self, if it wasn't. Skips any :autosave - # enabled records if they're marked_for_destruction?. + # enabled records if they're marked_for_destruction? or destroyed. def association_valid?(reflection, association) - return true if association.destroyed? + return true if association.destroyed? || association.marked_for_destruction? unless valid = association.valid? if reflection.options[:autosave] - unless association.marked_for_destruction? - association.errors.each_error do |attribute, error| - error = error.dup - error.attribute = "#{reflection.name}_#{attribute}" - errors.add(error) unless errors.on(error.attribute) - end + association.errors.each_error do |attribute, error| + error = error.dup + error.attribute = "#{reflection.name}_#{attribute}" + errors.add(error) unless errors.on(error.attribute) end else errors.add(reflection.name) diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 92e27e2..2c67bb3 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -537,6 +537,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase assert !@pirate.valid? @pirate.ship.mark_for_destruction + @pirate.ship.expects(:valid?).never assert_difference('Ship.count', -1) { @pirate.save! } end @@ -574,6 +575,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase assert !@ship.valid? @ship.pirate.mark_for_destruction + @ship.pirate.expects(:valid?).never assert_difference('Pirate.count', -1) { @ship.save! } end @@ -617,7 +619,10 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase children.each { |child| child.name = '' } assert !@pirate.valid? - children.each { |child| child.mark_for_destruction } + children.each do |child| + child.mark_for_destruction + child.expects(:valid?).never + end assert_difference("#{association_name.classify}.count", -2) { @pirate.save! } end -- 1.6.4.2 From 11c338735c3f6e9562c4c82ccd8913dd8b7a6a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 11 Jul 2009 19:01:21 +0200 Subject: [PATCH 301/779] Rename nested attributes _delete to _destroy to reflect its actual behavior and DSL (:allow_destroy). Deprecation warning added. [#2889 state:resolved] Signed-off-by: Eloy Duran --- .../lib/active_record/nested_attributes.rb | 55 +++++++++------ activerecord/test/cases/nested_attributes_test.rb | 72 +++++++++++--------- 2 files changed, 72 insertions(+), 55 deletions(-) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 9b4b954..b583e62 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -62,10 +62,10 @@ module ActiveRecord # accepts_nested_attributes_for :avatar, :allow_destroy => true # end # - # Now, when you add the _delete key to the attributes hash, with a + # Now, when you add the _destroy key to the attributes hash, with a # value that evaluates to +true+, you will destroy the associated model: # - # member.avatar_attributes = { :id => '2', :_delete => '1' } + # member.avatar_attributes = { :id => '2', :_destroy => '1' } # member.avatar.marked_for_destruction? # => true # member.save # member.avatar #=> nil @@ -85,14 +85,14 @@ module ActiveRecord # the attribute hash. # # For each hash that does _not_ have an id key a new record will - # be instantiated, unless the hash also contains a _delete key + # be instantiated, unless the hash also contains a _destroy key # that evaluates to +true+. # # params = { :member => { # :name => 'joe', :posts_attributes => [ # { :title => 'Kari, the awesome Ruby documentation browser!' }, # { :title => 'The egalitarian assumption of the modern citizen' }, - # { :title => '', :_delete => '1' } # this will be ignored + # { :title => '', :_destroy => '1' } # this will be ignored # ] # }} # @@ -140,7 +140,7 @@ module ActiveRecord # By default the associated records are protected from being destroyed. If # you want to destroy any of the associated records through the attributes # hash, you have to enable it first using the :allow_destroy - # option. This will allow you to also use the _delete key to + # option. This will allow you to also use the _destroy key to # destroy existing records: # # class Member < ActiveRecord::Base @@ -149,7 +149,7 @@ module ActiveRecord # end # # params = { :member => { - # :posts_attributes => [{ :id => '2', :_delete => '1' }] + # :posts_attributes => [{ :id => '2', :_destroy => '1' }] # }} # # member.attributes = params['member'] @@ -172,14 +172,14 @@ module ActiveRecord # Supported options: # [:allow_destroy] # If true, destroys any members from the attributes hash with a - # _delete key and a value that evaluates to +true+ + # _destroy key and a value that evaluates to +true+ # (eg. 1, '1', true, or 'true'). This option is off by default. # [:reject_if] # Allows you to specify a Proc that checks whether a record should be # built for a certain attribute hash. The hash is passed to the Proc # and the Proc should return either +true+ or +false+. When no Proc - # is specified a record will be built for all attribute hashes that - # do not have a _delete that evaluates to true. + # is specified, a record will be built for all attribute hashes that + # do not have a _destroy value that evaluates to true. # # Examples: # # creates avatar_attributes= @@ -223,15 +223,25 @@ module ActiveRecord # destruction of this association. # # See ActionView::Helpers::FormHelper::fields_for for more info. - def _delete + def _destroy marked_for_destruction? end + # Deal with deprecated _delete. + # + def _delete #:nodoc: + ActiveSupport::Deprecation.warn "_delete is deprecated in nested attributes. Use _destroy instead." + _destroy + end + private # Attribute hash keys that should not be assigned as normal attributes. # These hash keys are nested attributes implementation details. - UNASSIGNABLE_KEYS = %w{ id _delete } + # + # TODO Remove _delete from UNASSIGNABLE_KEYS when deprecation warning are + # removed. + UNASSIGNABLE_KEYS = %w( id _destroy _delete ) # Assigns the given attributes to the association. # @@ -240,7 +250,7 @@ module ActiveRecord # record will be built. # # If the given attributes include a matching :id attribute _and_ a - # :_delete key set to a truthy value, then the existing record + # :_destroy key set to a truthy value, then the existing record # will be marked for destruction. def assign_nested_attributes_for_one_to_one_association(association_name, attributes, allow_destroy) attributes = attributes.stringify_keys @@ -264,7 +274,7 @@ module ActiveRecord # Hashes with an :id value matching an existing associated record # will update that record. Hashes without an :id value will build # a new record for the association. Hashes with a matching :id - # value and a :_delete key set to a truthy value will mark the + # value and a :_destroy key set to a truthy value will mark the # matched record for destruction. # # For example: @@ -272,7 +282,7 @@ module ActiveRecord # assign_nested_attributes_for_collection_association(:people, { # '1' => { :id => '1', :name => 'Peter' }, # '2' => { :name => 'John' }, - # '3' => { :id => '2', :_delete => true } + # '3' => { :id => '2', :_destroy => true } # }) # # Will update the name of the Person with ID 1, build a new associated @@ -284,7 +294,7 @@ module ActiveRecord # assign_nested_attributes_for_collection_association(:people, [ # { :id => '1', :name => 'Peter' }, # { :name => 'John' }, - # { :id => '2', :_delete => true } + # { :id => '2', :_destroy => true } # ]) def assign_nested_attributes_for_collection_association(association_name, attributes_collection, allow_destroy) unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array) @@ -309,25 +319,26 @@ module ActiveRecord end # Updates a record with the +attributes+ or marks it for destruction if - # +allow_destroy+ is +true+ and has_delete_flag? returns +true+. + # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+. def assign_to_or_mark_for_destruction(record, attributes, allow_destroy) - if has_delete_flag?(attributes) && allow_destroy + if has_destroy_flag?(attributes) && allow_destroy record.mark_for_destruction else record.attributes = attributes.except(*UNASSIGNABLE_KEYS) end end - # Determines if a hash contains a truthy _delete key. - def has_delete_flag?(hash) - ConnectionAdapters::Column.value_to_boolean hash['_delete'] + # Determines if a hash contains a truthy _destroy key. + def has_destroy_flag?(hash) + ConnectionAdapters::Column.value_to_boolean(hash['_destroy']) || + ConnectionAdapters::Column.value_to_boolean(hash['_delete']) # TODO Remove after deprecation. end # Determines if a new record should be build by checking for - # has_delete_flag? or if a :reject_if proc exists for this + # has_destroy_flag? or if a :reject_if proc exists for this # association and evaluates to +true+. def reject_new_record?(association_name, attributes) - has_delete_flag?(attributes) || + has_destroy_flag?(attributes) || self.class.reject_new_nested_attributes_procs[association_name].try(:call, attributes) end end diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 7acdf43..792d6d1 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -49,15 +49,21 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase ship = pirate.create_ship(:name => 'Nights Dirty Lightning') assert_no_difference('Ship.count') do - pirate.update_attributes(:ship_attributes => { '_delete' => true }) + pirate.update_attributes(:ship_attributes => { '_destroy' => true }) end end - def test_a_model_should_respond_to_underscore_delete_and_return_if_it_is_marked_for_destruction + def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction ship = Ship.create!(:name => 'Nights Dirty Lightning') - assert !ship._delete + assert !ship._destroy ship.mark_for_destruction - assert ship._delete + assert ship._destroy + end + + def test_underscore_delete_is_deprecated + ActiveSupport::Deprecation.expects(:warn) + ship = Ship.create!(:name => 'Nights Dirty Lightning') + ship._delete end end @@ -87,9 +93,9 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name end - def test_should_not_build_a_new_record_if_there_is_no_id_and_delete_is_truthy + def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy @ship.destroy - @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_delete => '1' } + @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' } assert_nil @pirate.ship end @@ -109,8 +115,8 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase assert_equal 'Nights Dirty Lightning', @ship.name end - def test_should_not_replace_an_existing_record_if_there_is_no_id_and_delete_is_truthy - @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_delete => '1' } + def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy + @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' } assert_equal @ship, @pirate.ship assert_equal 'Nights Dirty Lightning', @pirate.ship.name @@ -137,29 +143,29 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name end - def test_should_delete_an_existing_record_if_there_is_a_matching_id_and_delete_is_truthy + def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy @pirate.ship.destroy [1, '1', true, 'true'].each do |truth| @pirate.reload.create_ship(:name => 'Mister Pablo') assert_difference('Ship.count', -1) do - @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => truth }) + @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => truth }) end end end - def test_should_not_delete_an_existing_record_if_delete_is_not_truthy + def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy [nil, '0', 0, 'false', false].each do |not_truth| assert_no_difference('Ship.count') do - @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => not_truth }) + @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => not_truth }) end end end - def test_should_not_delete_an_existing_record_if_allow_destroy_is_false + def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false Pirate.accepts_nested_attributes_for :ship, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? } assert_no_difference('Ship.count') do - @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => '1' }) + @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => '1' }) end Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } @@ -182,7 +188,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase def test_should_not_destroy_the_associated_model_until_the_parent_is_saved assert_no_difference('Ship.count') do - @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_delete => '1' } } + @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_destroy => '1' } } end assert_difference('Ship.count', -1) do @pirate.save @@ -213,9 +219,9 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase assert_equal 'Arr', @ship.pirate.catchphrase end - def test_should_not_build_a_new_record_if_there_is_no_id_and_delete_is_truthy + def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy @pirate.destroy - @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_delete => '1' } + @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' } assert_nil @ship.pirate end @@ -235,8 +241,8 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase assert_equal 'Aye', @pirate.catchphrase end - def test_should_not_replace_an_existing_record_if_there_is_no_id_and_delete_is_truthy - @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_delete => '1' } + def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy + @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' } assert_equal @pirate, @ship.pirate assert_equal 'Aye', @ship.pirate.catchphrase @@ -263,29 +269,29 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase assert_equal 'Arr', @ship.pirate.catchphrase end - def test_should_delete_an_existing_record_if_there_is_a_matching_id_and_delete_is_truthy + def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy @ship.pirate.destroy [1, '1', true, 'true'].each do |truth| @ship.reload.create_pirate(:catchphrase => 'Arr') assert_difference('Pirate.count', -1) do - @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => truth }) + @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => truth }) end end end - def test_should_not_delete_an_existing_record_if_delete_is_not_truthy + def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy [nil, '0', 0, 'false', false].each do |not_truth| assert_no_difference('Pirate.count') do - @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => not_truth }) + @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => not_truth }) end end end - def test_should_not_delete_an_existing_record_if_allow_destroy_is_false + def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false Ship.accepts_nested_attributes_for :pirate, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? } assert_no_difference('Pirate.count') do - @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => '1' }) + @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => '1' }) end Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } @@ -301,7 +307,7 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase def test_should_not_destroy_the_associated_model_until_the_parent_is_saved assert_no_difference('Pirate.count') do - @ship.attributes = { :pirate_attributes => { :id => @ship.pirate.id, '_delete' => true } } + @ship.attributes = { :pirate_attributes => { :id => @ship.pirate.id, '_destroy' => true } } end assert_difference('Pirate.count', -1) { @ship.save } end @@ -369,18 +375,18 @@ module NestedAttributesOnACollectionAssociationTests assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name end - def test_should_not_assign_delete_key_to_a_record + def test_should_not_assign_destroy_key_to_a_record assert_nothing_raised ActiveRecord::UnknownAttributeError do - @pirate.send(association_setter, { 'foo' => { '_delete' => '0' }}) + @pirate.send(association_setter, { 'foo' => { '_destroy' => '0' }}) end end - def test_should_ignore_new_associated_records_with_truthy_delete_attribute + def test_should_ignore_new_associated_records_with_truthy_destroy_attribute @pirate.send(@association_name).destroy_all @pirate.reload.attributes = { association_getter => { 'foo' => { :name => 'Grace OMalley' }, - 'bar' => { :name => 'Privateers Greed', '_delete' => '1' } + 'bar' => { :name => 'Privateers Greed', '_destroy' => '1' } } } @@ -432,7 +438,7 @@ module NestedAttributesOnACollectionAssociationTests ['1', 1, 'true', true].each do |true_variable| record = @pirate.reload.send(@association_name).create!(:name => 'Grace OMalley') @pirate.send(association_setter, - @alternate_params[association_getter].merge('baz' => { :id => record.id, '_delete' => true_variable }) + @alternate_params[association_getter].merge('baz' => { :id => record.id, '_destroy' => true_variable }) ) assert_difference('@pirate.send(@association_name).count', -1) do @@ -443,7 +449,7 @@ module NestedAttributesOnACollectionAssociationTests def test_should_not_destroy_the_associated_model_with_a_non_truthy_argument [nil, '', '0', 0, 'false', false].each do |false_variable| - @alternate_params[association_getter]['foo']['_delete'] = false_variable + @alternate_params[association_getter]['foo']['_destroy'] = false_variable assert_no_difference('@pirate.send(@association_name).count') do @pirate.update_attributes(@alternate_params) end @@ -452,7 +458,7 @@ module NestedAttributesOnACollectionAssociationTests def test_should_not_destroy_the_associated_model_until_the_parent_is_saved assert_no_difference('@pirate.send(@association_name).count') do - @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_delete' => true })) + @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_destroy' => true })) end assert_difference('@pirate.send(@association_name).count', -1) { @pirate.save } end -- 1.6.4.2 From a070873771ebede9097735f07fc40720dce89c46 Mon Sep 17 00:00:00 2001 From: Graeme Porteous Date: Sat, 11 Jul 2009 20:04:18 +0200 Subject: [PATCH 302/779] Fix has_one with foreign_key and primary_key association bug which caused the associated object being lost when saving the owner. [#1756 state:resolved] Mixed in a bit from patch by ransom-briggs. [#2813 state:resolved] Signed-off-by: Eloy Duran --- .../lib/active_record/autosave_association.rb | 9 ++++++--- .../associations/has_one_associations_test.rb | 9 +++++++++ activerecord/test/cases/reflection_test.rb | 4 ++-- activerecord/test/fixtures/accounts.yml | 1 + activerecord/test/models/company.rb | 1 + activerecord/test/schema/schema.rb | 1 + 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 58293fc..f352016 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -316,9 +316,12 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy - elsif autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave) - association[reflection.primary_key_name] = id - association.save(!autosave) + else + key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id + if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave) + association[reflection.primary_key_name] = key + association.save(!autosave) + end end end end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 1ddb3f4..aceda3f 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -36,6 +36,15 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_equal accounts(:rails_core_account), firm.account_using_primary_key end + def test_update_with_foreign_and_primary_keys + firm = companies(:first_firm) + account = firm.account_using_foreign_and_primary_keys + assert_equal Account.find_by_firm_name(firm.name), account + firm.save + firm.reload + assert_equal account, firm.account_using_foreign_and_primary_keys + end + def test_can_marshal_has_one_association_with_nil_target firm = Firm.new assert_nothing_raised do diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index b18b99d..78bcc41 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -170,9 +170,9 @@ class ReflectionTest < ActiveRecord::TestCase def test_reflection_of_all_associations # FIXME these assertions bust a lot - assert_equal 33, Firm.reflect_on_all_associations.size + assert_equal 34, Firm.reflect_on_all_associations.size assert_equal 25, Firm.reflect_on_all_associations(:has_many).size - assert_equal 8, Firm.reflect_on_all_associations(:has_one).size + assert_equal 9, Firm.reflect_on_all_associations(:has_one).size assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size end diff --git a/activerecord/test/fixtures/accounts.yml b/activerecord/test/fixtures/accounts.yml index b2d0191..3258304 100644 --- a/activerecord/test/fixtures/accounts.yml +++ b/activerecord/test/fixtures/accounts.yml @@ -2,6 +2,7 @@ signals37: id: 1 firm_id: 1 credit_limit: 50 + firm_name: 37signals unknown: id: 2 diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 16e8484..1a4f777 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -72,6 +72,7 @@ class Firm < Company has_one :account_with_select, :foreign_key => "firm_id", :select => "id, firm_id", :class_name=>'Account' has_one :readonly_account, :foreign_key => "firm_id", :class_name => "Account", :readonly => true has_one :account_using_primary_key, :primary_key => "firm_id", :class_name => "Account" + has_one :account_using_foreign_and_primary_keys, :foreign_key => "firm_name", :primary_key => "name", :class_name => "Account" has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete has_one :unautosaved_account, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 87fad0e..984c5ba 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -23,6 +23,7 @@ ActiveRecord::Schema.define do # unless the ordering matters. In which case, define them below create_table :accounts, :force => true do |t| t.integer :firm_id + t.string :firm_name t.integer :credit_limit end -- 1.6.4.2 From c665faac09f1ff5a1b943247091cf41762bd468b Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Sat, 11 Jul 2009 20:52:03 +0200 Subject: [PATCH 303/779] During autosave, ignore records that already have been destroyed. [#2537 state:resolved] --- .../lib/active_record/autosave_association.rb | 6 +++- .../test/cases/autosave_association_test.rb | 26 +++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index f352016..eee77a8 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -283,6 +283,8 @@ module ActiveRecord if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave) records.each do |record| + next if record.destroyed? + if autosave && record.marked_for_destruction? association.destroy(record) elsif autosave != false && (@new_record_before_save || record.new_record?) @@ -311,7 +313,7 @@ module ActiveRecord # This all happens inside a transaction, _if_ the Transactions module is included into # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. def save_has_one_association(reflection) - if (association = association_instance_get(reflection.name)) && !association.target.nil? + if (association = association_instance_get(reflection.name)) && !association.target.nil? && !association.destroyed? autosave = reflection.options[:autosave] if autosave && association.marked_for_destruction? @@ -335,7 +337,7 @@ module ActiveRecord # This all happens inside a transaction, _if_ the Transactions module is included into # ActiveRecord::Base after the AutosaveAssociation module, which it does by default. def save_belongs_to_association(reflection) - if association = association_instance_get(reflection.name) + if (association = association_instance_get(reflection.name)) && !association.destroyed? autosave = reflection.options[:autosave] if autosave && association.marked_for_destruction? diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 2c67bb3..75d40fb 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -541,6 +541,13 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase assert_difference('Ship.count', -1) { @pirate.save! } end + def test_a_child_marked_for_destruction_should_not_be_destroyed_twice + @pirate.ship.mark_for_destruction + assert @pirate.save + @pirate.ship.expects(:destroy).never + assert @pirate.save + end + def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_child # Stub the save method of the @pirate.ship instance to destroy and then raise an exception class << @pirate.ship @@ -579,6 +586,13 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase assert_difference('Pirate.count', -1) { @ship.save! } end + def test_a_parent_marked_for_destruction_should_not_be_destroyed_twice + @ship.pirate.mark_for_destruction + assert @ship.save + @ship.pirate.expects(:destroy).never + assert @ship.save + end + def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent # Stub the save method of the @ship.pirate instance to destroy and then raise an exception class << @ship.pirate @@ -637,6 +651,16 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase assert @pirate.valid? end + define_method("test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_#{association_name}") do + @pirate.send(association_name).create!(:name => "#{association_name}_1") + children = @pirate.send(association_name) + + children.each { |child| child.mark_for_destruction } + assert @pirate.save + children.each { |child| child.expects(:destroy).never } + assert @pirate.save + end + define_method("test_should_rollback_destructions_if_an_exception_occurred_while_saving_#{association_name}") do 2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") } before = @pirate.send(association_name).map { |c| c } @@ -1000,4 +1024,4 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T end include AutosaveAssociationOnACollectionAssociationTests -end \ No newline at end of file +end -- 1.6.4.2 From 9290051b852604c27520c4331fb96e7066f03c75 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Sat, 11 Jul 2009 21:01:56 +0200 Subject: [PATCH 304/779] Added some documentation about setting :autosave => false on an association. --- activerecord/lib/active_record/associations.rb | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a08119c..c739fdd 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -275,9 +275,10 @@ module ActiveRecord # You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be # aware of, mostly involving the saving of associated objects. # - # Unless you enable the :autosave option on a has_one, belongs_to, - # has_many, or has_and_belongs_to_many association, - # in which case the members are always saved. + # Unless you set the :autosave option on a has_one, belongs_to, + # has_many, or has_and_belongs_to_many association. Setting it + # to +true+ will _always_ save the members, whereas setting it to +false+ will + # _never_ save the members. # # === One-to-one associations # -- 1.6.4.2 From c0245493cb9316b2c92ee4a02e71de557eb31ff2 Mon Sep 17 00:00:00 2001 From: Alexey Kovyrin Date: Sat, 12 Sep 2009 14:55:34 +0200 Subject: [PATCH 305/779] Define autosave association validation methods only when needed. [#3161 state:resolved] Signed-off-by: Eloy Duran --- .../lib/active_record/autosave_association.rb | 13 ++- .../test/cases/autosave_association_test.rb | 115 ++++++++++++++++++++ activerecord/test/models/pirate.rb | 9 +- activerecord/test/models/ship.rb | 2 +- 4 files changed, 132 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index eee77a8..edf5704 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -159,7 +159,7 @@ module ActiveRecord def add_autosave_association_callbacks(reflection) save_method = "autosave_associated_records_for_#{reflection.name}" validation_method = "validate_associated_records_for_#{reflection.name}" - validate validation_method + force_validation = (reflection.options[:validate] == true || reflection.options[:autosave] == true) case reflection.macro when :has_many, :has_and_belongs_to_many @@ -170,7 +170,10 @@ module ActiveRecord after_create save_method after_update save_method - define_method(validation_method) { validate_collection_association(reflection) } + if force_validation || (reflection.macro == :has_many && reflection.options[:validate] != false) + define_method(validation_method) { validate_collection_association(reflection) } + validate validation_method + end else case reflection.macro when :has_one @@ -180,7 +183,11 @@ module ActiveRecord define_method(save_method) { save_belongs_to_association(reflection) } before_save save_method end - define_method(validation_method) { validate_single_association(reflection) } + + if force_validation + define_method(validation_method) { validate_single_association(reflection) } + validate validation_method + end end end end diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 75d40fb..d91f90a 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -1025,3 +1025,118 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T include AutosaveAssociationOnACollectionAssociationTests end + +class TestAutosaveAssociationValidationsOnAHasManyAssocication < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?") + @pirate.birds.create(:name => 'cookoo') + end + + test "should automatically validate associations" do + assert @pirate.valid? + @pirate.birds.each { |bird| bird.name = '' } + + assert !@pirate.valid? + end +end + +class TestAutosaveAssociationValidationsOnAHasOneAssocication < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?") + @pirate.create_ship(:name => 'titanic') + end + + test "should automatically validate associations with :validate => true" do + assert @pirate.valid? + @pirate.ship.name = '' + assert !@pirate.valid? + end + + test "should not automatically validate associations without :validate => true" do + assert @pirate.valid? + @pirate.non_validated_ship.name = '' + assert @pirate.valid? + end +end + +class TestAutosaveAssociationValidationsOnABelongsToAssocication < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?") + end + + test "should automatically validate associations with :validate => true" do + assert @pirate.valid? + @pirate.parrot = Parrot.new(:name => '') + assert !@pirate.valid? + end + + test "should not automatically validate associations without :validate => true" do + assert @pirate.valid? + @pirate.non_validated_parrot = Parrot.new(:name => '') + assert @pirate.valid? + end +end + +class TestAutosaveAssociationValidationsOnAHABTMAssocication < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?") + end + + test "should automatically validate associations with :validate => true" do + assert @pirate.valid? + @pirate.parrots = [ Parrot.new(:name => 'popuga') ] + @pirate.parrots.each { |parrot| parrot.name = '' } + assert !@pirate.valid? + end + + test "should not automatically validate associations without :validate => true" do + assert @pirate.valid? + @pirate.non_validated_parrots = [ Parrot.new(:name => 'popuga') ] + @pirate.non_validated_parrots.each { |parrot| parrot.name = '' } + assert @pirate.valid? + end +end + +class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.new + end + + test "should generate validation methods for has_many associations" do + assert @pirate.respond_to?(:validate_associated_records_for_birds) + end + + test "should generate validation methods for has_one associations with :validate => true" do + assert @pirate.respond_to?(:validate_associated_records_for_ship) + end + + test "should not generate validation methods for has_one associations without :validate => true" do + assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_ship) + end + + test "should generate validation methods for belongs_to associations with :validate => true" do + assert @pirate.respond_to?(:validate_associated_records_for_parrot) + end + + test "should not generate validation methods for belongs_to associations without :validate => true" do + assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrot) + end + + test "should generate validation methods for HABTM associations with :validate => true" do + assert @pirate.respond_to?(:validate_associated_records_for_parrots) + end + + test "should not generate validation methods for HABTM associations without :validate => true" do + assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrots) + end +end diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index 238917b..bcf23f0 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -1,6 +1,8 @@ class Pirate < ActiveRecord::Base - belongs_to :parrot - has_and_belongs_to_many :parrots + belongs_to :parrot, :validate => true + belongs_to :non_validated_parrot, :class_name => 'Parrot' + has_and_belongs_to_many :parrots, :validate => true + has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot' has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot", :before_add => :log_before_add, :after_add => :log_after_add, @@ -16,7 +18,8 @@ class Pirate < ActiveRecord::Base has_many :treasure_estimates, :through => :treasures, :source => :price_estimates # These both have :autosave enabled because accepts_nested_attributes_for is used on them. - has_one :ship + has_one :ship, :validate => true + has_one :non_validated_ship, :class_name => 'Ship' has_many :birds has_many :birds_with_method_callbacks, :class_name => "Bird", :before_add => :log_before_add, diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb index 06759d6..d0df951 100644 --- a/activerecord/test/models/ship.rb +++ b/activerecord/test/models/ship.rb @@ -1,7 +1,7 @@ class Ship < ActiveRecord::Base self.record_timestamps = false - belongs_to :pirate + belongs_to :pirate, :validate => true has_many :parts, :class_name => 'ShipPart', :autosave => true accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } -- 1.6.4.2 From 2524ac84e6ae165082e764e94cdbbced069e43b3 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Sat, 12 Sep 2009 15:03:05 +0200 Subject: [PATCH 306/779] Removed some superfluous conditionals from the autosave association validation methods. Which are unneeded now that we only define them when needed. --- .../lib/active_record/autosave_association.rb | 8 +++----- 1 files changed, 3 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index edf5704..d7fdb0b 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -231,10 +231,8 @@ module ActiveRecord # Validate the association if :validate or :autosave is # turned on for the association specified by +reflection+. def validate_single_association(reflection) - if reflection.options[:validate] == true || reflection.options[:autosave] == true - if (association = association_instance_get(reflection.name)) && !association.target.nil? - association_valid?(reflection, association) - end + if (association = association_instance_get(reflection.name)) && !association.target.nil? + association_valid?(reflection, association) end end @@ -242,7 +240,7 @@ module ActiveRecord # :autosave is turned on for the association specified by # +reflection+. def validate_collection_association(reflection) - if reflection.options[:validate] != false && association = association_instance_get(reflection.name) + if association = association_instance_get(reflection.name) if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave]) records.each { |record| association_valid?(reflection, record) } end -- 1.6.4.2 From 6ddb7de407a1330bff9383e9587c9a01fc69d7ee Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 13 Sep 2009 10:37:41 +1200 Subject: [PATCH 307/779] Dup the arguments to string compare so we can use force_encoding. Conflicts: activesupport/lib/active_support/message_verifier.rb --- .../lib/active_support/message_verifier.rb | 13 ++++++++----- 1 files changed, 8 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index 5596784..e76a03f 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -38,14 +38,17 @@ module ActiveSupport end private - if "foo".respond_to?(:bytesize) + if "foo".respond_to?(:force_encoding) # constant-time comparison algorithm to prevent timing attacks - # > 1.8.6 friendly version def secure_compare(a, b) - if a.bytesize == b.bytesize + a = a.dup.force_encoding(Encoding::BINARY) + b = b.dup.force_encoding(Encoding::BINARY) + + if a.length == b.length result = 0 - j = b.each_byte - a.each_byte { |i| result |= i ^ j.next } + for i in 0..(a.length - 1) + result |= a[i].ord ^ b[i].ord + end result == 0 else false -- 1.6.4.2 From 8afeec20e0f664613ddf9e1f965c1d8d5262b8ea Mon Sep 17 00:00:00 2001 From: sdsykes Date: Mon, 7 Sep 2009 13:22:19 +0300 Subject: [PATCH 308/779] Ruby 1.9 compat: corrected instance_methods check [#3156 state:committed] Signed-off-by: Jeremy Kemper --- .../connection_adapters/mysql_adapter.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 1259278..022aa7a 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -7,7 +7,8 @@ module MysqlCompat #:nodoc: raise 'Mysql not loaded' unless defined?(::Mysql) target = defined?(Mysql::Result) ? Mysql::Result : MysqlRes - return if target.instance_methods.include?('all_hashes') + return if target.instance_methods.include?('all_hashes') || + target.instance_methods.include?(:all_hashes) # Ruby driver has a version string and returns null values in each_hash # C driver >= 2.7 returns null values in each_hash -- 1.6.4.2 From 477dfa4c798e88971f6a546dd8dd7c83e223daed Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 13 Sep 2009 04:43:41 -0700 Subject: [PATCH 309/779] Use Encoding.default_external, not _internal --- .../lib/active_support/multibyte/utils.rb | 4 ++-- activesupport/test/multibyte_utils_test.rb | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/activesupport/lib/active_support/multibyte/utils.rb b/activesupport/lib/active_support/multibyte/utils.rb index a7e4068..8e47763 100644 --- a/activesupport/lib/active_support/multibyte/utils.rb +++ b/activesupport/lib/active_support/multibyte/utils.rb @@ -5,7 +5,7 @@ module ActiveSupport #:nodoc: if Kernel.const_defined?(:Encoding) # Returns a regular expression that matches valid characters in the current encoding def self.valid_character - VALID_CHARACTER[Encoding.default_internal.to_s] + VALID_CHARACTER[Encoding.default_external.to_s] end else def self.valid_character @@ -58,4 +58,4 @@ module ActiveSupport #:nodoc: end end end -end \ No newline at end of file +end diff --git a/activesupport/test/multibyte_utils_test.rb b/activesupport/test/multibyte_utils_test.rb index d8ac5ff..dea9e74 100644 --- a/activesupport/test/multibyte_utils_test.rb +++ b/activesupport/test/multibyte_utils_test.rb @@ -103,11 +103,11 @@ class MultibyteUtilsTest < ActiveSupport::TestCase if Kernel.const_defined?(:Encoding) def example(key) - STRINGS[key].force_encoding(Encoding.default_internal) + STRINGS[key].force_encoding(Encoding.default_external) end def examples - STRINGS.values.map { |s| s.force_encoding(Encoding.default_internal) } + STRINGS.values.map { |s| s.force_encoding(Encoding.default_external) } end else def example(key) @@ -121,21 +121,21 @@ class MultibyteUtilsTest < ActiveSupport::TestCase if 'string'.respond_to?(:encoding) def with_encoding(enc) - before = Encoding.default_internal + before = Encoding.default_external case enc when 'UTF8' - Encoding.default_internal = Encoding::UTF_8 + Encoding.default_external = Encoding::UTF_8 when 'SJIS' - Encoding.default_internal = Encoding::Shift_JIS + Encoding.default_external = Encoding::Shift_JIS else - Encoding.default_internal = Encoding::BINARY + Encoding.default_external = Encoding::BINARY end yield - Encoding.default_internal = before + Encoding.default_external = before end else alias with_encoding with_kcode end -end \ No newline at end of file +end -- 1.6.4.2 From 8dca666ba1714a497d7f83c7c97ec876c3d82530 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 13 Sep 2009 04:48:40 -0700 Subject: [PATCH 310/779] Silence warning for Encoding.default_external= --- activesupport/test/multibyte_utils_test.rb | 14 +++++--------- 1 files changed, 5 insertions(+), 9 deletions(-) diff --git a/activesupport/test/multibyte_utils_test.rb b/activesupport/test/multibyte_utils_test.rb index dea9e74..0a2f20d 100644 --- a/activesupport/test/multibyte_utils_test.rb +++ b/activesupport/test/multibyte_utils_test.rb @@ -120,20 +120,16 @@ class MultibyteUtilsTest < ActiveSupport::TestCase end if 'string'.respond_to?(:encoding) + KCODE_TO_ENCODING = Hash.new(Encoding::BINARY). + update('UTF8' => Encoding::UTF_8, 'SJIS' => Encoding::Shift_JIS) + def with_encoding(enc) before = Encoding.default_external + silence_warnings { Encoding.default_external = KCODE_TO_ENCODING[enc] } - case enc - when 'UTF8' - Encoding.default_external = Encoding::UTF_8 - when 'SJIS' - Encoding.default_external = Encoding::Shift_JIS - else - Encoding.default_external = Encoding::BINARY - end yield - Encoding.default_external = before + silence_warnings { Encoding.default_external = before } end else alias with_encoding with_kcode -- 1.6.4.2 From c6fe042b296032e87bddb159c1cd225489abf55c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 22 Aug 2009 15:53:19 -0700 Subject: [PATCH 311/779] Ruby 1.9: fix Time#beginning_of_day inaccuracy due to subtracting a Float --- .../active_support/core_ext/time/calculations.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index dfc48f4..380922d 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -201,7 +201,8 @@ module ActiveSupport #:nodoc: # Returns a new Time representing the start of the day (0:00) def beginning_of_day - (self - self.seconds_since_midnight).change(:usec => 0) + #(self - seconds_since_midnight).change(:usec => 0) + change(:hour => 0, :min => 0, :sec => 0, :usec => 0) end alias :midnight :beginning_of_day alias :at_midnight :beginning_of_day -- 1.6.4.2 From 09b197f9576b71200c8537f2da6df1f891bbb931 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 13 Sep 2009 05:30:59 -0700 Subject: [PATCH 312/779] Ruby 1.9 compat: fix regexp slice test --- activesupport/test/multibyte_chars_test.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index 05eee42..fdaea32 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -391,7 +391,7 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase assert_equal 'ちわ', @chars.slice(2..10) assert_equal '', @chars.slice(4..10) assert_equal 'に', @chars.slice(/に/u) - assert_equal 'にち', @chars.slice(/に\w/u) + assert_equal 'にち', @chars.slice(/に./u) assert_equal nil, @chars.slice(/unknown/u) assert_equal 'にち', @chars.slice(/(にち)/u, 1) assert_equal nil, @chars.slice(/(にち)/u, 2) -- 1.6.4.2 From abd7bd311ae9935bfba5657a4f7d5d637c830d62 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 13 Sep 2009 05:43:02 -0700 Subject: [PATCH 313/779] Clean up spurious JSON decoding test failure --- .../lib/active_support/json/backends/jsongem.rb | 5 ++--- .../lib/active_support/json/backends/yaml.rb | 6 +----- activesupport/lib/active_support/json/decoding.rb | 2 ++ activesupport/test/json/decoding_test.rb | 10 ++-------- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb index a6da27d..4076db9 100644 --- a/activesupport/lib/active_support/json/backends/jsongem.rb +++ b/activesupport/lib/active_support/json/backends/jsongem.rb @@ -2,10 +2,9 @@ require 'json' unless defined?(JSON) module ActiveSupport module JSON - ParseError = ::JSON::ParserError unless const_defined?(:ParseError) - module Backends module JSONGem + ParseError = ::JSON::ParserError extend self # Converts a JSON string into a Ruby object. @@ -35,4 +34,4 @@ module ActiveSupport end end end -end \ No newline at end of file +end diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb index 8c5b697..ab2ec46 100644 --- a/activesupport/lib/active_support/json/backends/yaml.rb +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -2,13 +2,9 @@ require 'active_support/core_ext/string/starts_ends_with' module ActiveSupport module JSON - unless const_defined?(:ParseError) - class ParseError < StandardError - end - end - module Backends module Yaml + ParseError = ::StandardError extend self # Converts a JSON string into a Ruby object. diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index b4e4177..b88a9a7 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -6,6 +6,7 @@ module ActiveSupport module JSON class << self + attr_reader :parse_error delegate :decode, :to => :backend def backend @@ -20,6 +21,7 @@ module ActiveSupport require "active_support/json/backends/#{name.to_s.downcase}.rb" @backend = ActiveSupport::JSON::Backends::const_get(name) end + @parse_error = @backend::ParseError end def with_backend(name) diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index d0d0ec7..5cf7b53 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -39,13 +39,7 @@ class TestJSONDecoding < ActiveSupport::TestCase ActiveSupport::JSON.backend backends = %w(Yaml) - begin - gem 'json', '>= 1.1' - require 'json' - backends << "JSONGem" - rescue Gem::LoadError - # Skip JSON gem tests - end + backends << "JSONGem" if defined?(::JSON) backends.each do |backend| TESTS.each do |json, expected| @@ -73,7 +67,7 @@ class TestJSONDecoding < ActiveSupport::TestCase end def test_failed_json_decoding - assert_raise(ActiveSupport::JSON::ParseError) { ActiveSupport::JSON.decode(%({: 1})) } + assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({: 1})) } end end -- 1.6.4.2 From f503a483d4bb90017ec108ed8644d9dec97bac8e Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 9 Aug 2009 12:14:25 -0700 Subject: [PATCH 314/779] Extract repetitive method --- activesupport/test/dependencies_test.rb | 70 ++++++++++++++++-------------- 1 files changed, 37 insertions(+), 33 deletions(-) diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index a21f094..c9b0e73 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -30,6 +30,10 @@ class DependenciesTest < Test::Unit::TestCase ActiveSupport::Dependencies.explicitly_unloadable_constants = [] end + def with_autoloading_fixtures(&block) + with_loading 'autoloading_fixtures', &block + end + def test_tracking_loaded_files require_dependency 'dependencies/service_one' require_dependency 'dependencies/service_two' @@ -126,7 +130,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_module_loading - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert_kind_of Module, A assert_kind_of Class, A::B assert_kind_of Class, A::C::D @@ -135,7 +139,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_non_existing_const_raises_name_error - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert_raise(NameError) { DoesNotExist } assert_raise(NameError) { NoModule::DoesNotExist } assert_raise(NameError) { A::DoesNotExist } @@ -144,49 +148,49 @@ class DependenciesTest < Test::Unit::TestCase end def test_directories_manifest_as_modules_unless_const_defined - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert_kind_of Module, ModuleFolder Object.__send__ :remove_const, :ModuleFolder end end def test_module_with_nested_class - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert_kind_of Class, ModuleFolder::NestedClass Object.__send__ :remove_const, :ModuleFolder end end def test_module_with_nested_inline_class - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert_kind_of Class, ModuleFolder::InlineClass Object.__send__ :remove_const, :ModuleFolder end end def test_directories_may_manifest_as_nested_classes - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert_kind_of Class, ClassFolder Object.__send__ :remove_const, :ClassFolder end end def test_class_with_nested_class - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert_kind_of Class, ClassFolder::NestedClass Object.__send__ :remove_const, :ClassFolder end end def test_class_with_nested_inline_class - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert_kind_of Class, ClassFolder::InlineClass Object.__send__ :remove_const, :ClassFolder end end def test_class_with_nested_inline_subclass_of_parent - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert_kind_of Class, ClassFolder::ClassFolderSubclass assert_kind_of Class, ClassFolder assert_equal 'indeed', ClassFolder::ClassFolderSubclass::ConstantInClassFolder @@ -195,7 +199,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_nested_class_can_access_sibling - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do sibling = ModuleFolder::NestedClass.class_eval "NestedSibling" assert defined?(ModuleFolder::NestedSibling) assert_equal ModuleFolder::NestedSibling, sibling @@ -204,7 +208,7 @@ class DependenciesTest < Test::Unit::TestCase end def failing_test_access_thru_and_upwards_fails - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert ! defined?(ModuleFolder) assert_raise(NameError) { ModuleFolder::Object } assert_raise(NameError) { ModuleFolder::NestedClass::Object } @@ -213,7 +217,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_non_existing_const_raises_name_error_with_fully_qualified_name - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do begin A::DoesNotExist.nil? flunk "No raise!!" @@ -291,7 +295,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_autoloaded? - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder") assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") @@ -370,7 +374,7 @@ class DependenciesTest < Test::Unit::TestCase end end_eval - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert_kind_of Integer, ::ModuleWithCustomConstMissing::B assert_kind_of Module, ::ModuleWithCustomConstMissing::A assert_kind_of String, ::ModuleWithCustomConstMissing::A::B @@ -379,7 +383,7 @@ class DependenciesTest < Test::Unit::TestCase def test_const_missing_should_not_double_load $counting_loaded_times = 0 - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do require_dependency '././counting_loader' assert_equal 1, $counting_loaded_times assert_raise(ArgumentError) { ActiveSupport::Dependencies.load_missing_constant Object, :CountingLoader } @@ -393,7 +397,7 @@ class DependenciesTest < Test::Unit::TestCase m.module_eval "def a() CountingLoader; end" extend m kls = nil - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do kls = nil assert_nothing_raised { kls = a } assert_equal "CountingLoader", kls.name @@ -428,7 +432,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_load_once_paths_do_not_add_to_autoloaded_constants - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do ActiveSupport::Dependencies.load_once_paths = ActiveSupport::Dependencies.load_paths.dup assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder") @@ -444,7 +448,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_application_should_special_case_application_controller - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do require_dependency 'application' assert_equal 10, ApplicationController assert ActiveSupport::Dependencies.autoloaded?(:ApplicationController) @@ -452,7 +456,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_const_missing_on_kernel_should_fallback_to_object - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do kls = Kernel::E assert_equal "E", kls.name assert_equal kls.object_id, Kernel::E.object_id @@ -460,14 +464,14 @@ class DependenciesTest < Test::Unit::TestCase end def test_preexisting_constants_are_not_marked_as_autoloaded - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do require_dependency 'e' assert ActiveSupport::Dependencies.autoloaded?(:E) ActiveSupport::Dependencies.clear end Object.const_set :E, Class.new - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do require_dependency 'e' assert ! ActiveSupport::Dependencies.autoloaded?(:E), "E shouldn't be marked autoloaded!" ActiveSupport::Dependencies.clear @@ -478,7 +482,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_unloadable - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do Object.const_set :M, Module.new M.unloadable @@ -492,14 +496,14 @@ class DependenciesTest < Test::Unit::TestCase end def test_unloadable_should_fail_with_anonymous_modules - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do m = Module.new assert_raise(ArgumentError) { m.unloadable } end end def test_unloadable_should_return_change_flag - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do Object.const_set :M, Module.new assert_equal true, M.unloadable assert_equal false, M.unloadable @@ -590,7 +594,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_file_with_multiple_constants_and_require_dependency - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert ! defined?(MultipleConstantFile) assert ! defined?(SiblingConstant) @@ -608,7 +612,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_file_with_multiple_constants_and_auto_loading - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert ! defined?(MultipleConstantFile) assert ! defined?(SiblingConstant) @@ -627,7 +631,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_nested_file_with_multiple_constants_and_require_dependency - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert ! defined?(ClassFolder::NestedClass) assert ! defined?(ClassFolder::SiblingClass) @@ -646,7 +650,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_nested_file_with_multiple_constants_and_auto_loading - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert ! defined?(ClassFolder::NestedClass) assert ! defined?(ClassFolder::SiblingClass) @@ -665,7 +669,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_autoload_doesnt_shadow_no_method_error_with_relative_constant - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it hasn't been referenced yet!" 2.times do assert_raise(NoMethodError) { RaisesNoMethodError } @@ -678,7 +682,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_autoload_doesnt_shadow_no_method_error_with_absolute_constant - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it hasn't been referenced yet!" 2.times do assert_raise(NoMethodError) { ::RaisesNoMethodError } @@ -691,7 +695,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_autoload_doesnt_shadow_error_when_mechanism_not_set_to_load - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do ActiveSupport::Dependencies.mechanism = :require 2.times do assert_raise(NameError) { assert_equal 123, ::RaisesNameError::FooBarBaz } @@ -700,7 +704,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_autoload_doesnt_shadow_name_error - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do Object.send(:remove_const, :RaisesNameError) if defined?(::RaisesNameError) 2.times do begin @@ -734,7 +738,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_load_once_constants_should_not_be_unloaded - with_loading 'autoloading_fixtures' do + with_autoloading_fixtures do ActiveSupport::Dependencies.load_once_paths = ActiveSupport::Dependencies.load_paths ::A.to_s assert defined?(A) -- 1.6.4.2 From ff0377dea594bd24101658c3aceb45b1b9392bfb Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 9 Aug 2009 12:40:16 -0700 Subject: [PATCH 315/779] Fix failing dependencies test relying on . being in LOAD_PATH --- activesupport/test/dependencies_test.rb | 6 ++++-- 1 files changed, 4 insertions(+), 2 deletions(-) diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index c9b0e73..1edc7ce 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -20,9 +20,11 @@ class DependenciesTest < Test::Unit::TestCase def with_loading(*from) old_mechanism, ActiveSupport::Dependencies.mechanism = ActiveSupport::Dependencies.mechanism, :load - dir = File.dirname(__FILE__) + this_dir = File.dirname(__FILE__) + parent_dir = File.dirname(this_dir) + $LOAD_PATH.unshift(parent_dir) unless $LOAD_PATH.include?(parent_dir) prior_load_paths = ActiveSupport::Dependencies.load_paths - ActiveSupport::Dependencies.load_paths = from.collect { |f| "#{dir}/#{f}" } + ActiveSupport::Dependencies.load_paths = from.collect { |f| "#{this_dir}/#{f}" } yield ensure ActiveSupport::Dependencies.load_paths = prior_load_paths -- 1.6.4.2 From 179b4512d1e98c7657151b25f159262ea1860f52 Mon Sep 17 00:00:00 2001 From: Developer Date: Wed, 16 Sep 2009 14:12:13 -0400 Subject: [PATCH 316/779] Allow Nokogiri XmlMini backend to process cdata elements [#3219 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/active_support/xml_mini/nokogiri.rb | 2 +- .../test/xml_mini/nokogiri_engine_test.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 1028158..a77a7f9 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -38,7 +38,7 @@ module ActiveSupport walker = lambda { |memo, parent, child, callback| next if child.blank? && 'file' != parent['type'] - if child.text? + if child.text? || child.cdata? (memo[CONTENT_ROOT] ||= '') << child.content next end diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb index e5174a0..09989f2 100644 --- a/activesupport/test/xml_mini/nokogiri_engine_test.rb +++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb @@ -147,6 +147,17 @@ class NokogiriEngineTest < Test::Unit::TestCase eoxml end + def test_children_with_cdata + assert_equal_rexml(<<-eoxml) + + + hello + morning + + + eoxml + end + private def assert_equal_rexml(xml) hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } -- 1.6.4.2 From 4f5cac53b7ce2e8694c836a9dfe3619f9f7c9ccc Mon Sep 17 00:00:00 2001 From: Justin Bailey Date: Mon, 14 Sep 2009 17:53:04 -0700 Subject: [PATCH 317/779] Enable use of MySQL stored procedures by default. [#3204 state:committed] Signed-off-by: Jeremy Kemper --- .../connection_adapters/mysql_adapter.rb | 5 ++++- activerecord/test/cases/connection_test_mysql.rb | 8 ++++++++ activerecord/test/schema/mysql_specific_schema.rb | 12 ++++++++++++ 3 files changed, 24 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 022aa7a..19345d3 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -64,12 +64,15 @@ module ActiveRecord raise end end + MysqlCompat.define_all_hashes_method! mysql = Mysql.init mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey] - ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config) + default_flags = Mysql.const_defined?(:CLIENT_MULTI_RESULTS) ? Mysql::CLIENT_MULTI_RESULTS : 0 + options = [host, username, password, database, port, socket, default_flags] + ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config) end end diff --git a/activerecord/test/cases/connection_test_mysql.rb b/activerecord/test/cases/connection_test_mysql.rb index f79ee2f..8e4842a 100644 --- a/activerecord/test/cases/connection_test_mysql.rb +++ b/activerecord/test/cases/connection_test_mysql.rb @@ -41,6 +41,14 @@ class MysqlConnectionTest < ActiveRecord::TestCase sleep 2 @connection.verify! assert @connection.active? + end + + # Test that MySQL allows multiple results for stored procedures + if Mysql.const_defined?(:CLIENT_MULTI_RESULTS) + def test_multi_results + rows = ActiveRecord::Base.connection.select_rows('CALL ten();') + assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}" + end end private diff --git a/activerecord/test/schema/mysql_specific_schema.rb b/activerecord/test/schema/mysql_specific_schema.rb index f44c33a..c78d99f 100644 --- a/activerecord/test/schema/mysql_specific_schema.rb +++ b/activerecord/test/schema/mysql_specific_schema.rb @@ -9,4 +9,16 @@ ActiveRecord::Schema.define do t.text :medium_text, :limit => 16777215 t.text :long_text, :limit => 2147483647 end + + ActiveRecord::Base.connection.execute <<-SQL +DROP PROCEDURE IF EXISTS ten; +SQL + + ActiveRecord::Base.connection.execute <<-SQL +CREATE PROCEDURE ten() SQL SECURITY INVOKER +BEGIN + select 10; +END +SQL + end -- 1.6.4.2 From 1488c6cc9e6237ce794e3c4a6201627b9fd4ca09 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 17 Sep 2009 11:34:40 -0700 Subject: [PATCH 318/779] Fix brittle content-type check. [#1956 state:committed] --- activeresource/lib/active_resource/validations.rb | 4 ++-- activeresource/test/base_errors_test.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/activeresource/lib/active_resource/validations.rb b/activeresource/lib/active_resource/validations.rb index ee377ac..56621dd 100644 --- a/activeresource/lib/active_resource/validations.rb +++ b/activeresource/lib/active_resource/validations.rb @@ -260,9 +260,9 @@ module ActiveResource true rescue ResourceInvalid => error case error.response['Content-Type'] - when 'application/xml' + when /xml/ errors.from_xml(error.response.body) - when 'application/json' + when /json/ errors.from_json(error.response.body) end false diff --git a/activeresource/test/base_errors_test.rb b/activeresource/test/base_errors_test.rb index c1b4867..21e0b39 100644 --- a/activeresource/test/base_errors_test.rb +++ b/activeresource/test/base_errors_test.rb @@ -4,8 +4,8 @@ require "fixtures/person" class BaseErrorsTest < Test::Unit::TestCase def setup ActiveResource::HttpMock.respond_to do |mock| - mock.post "/people.xml", {}, %q(Age can't be blankName can't be blankName must start with a letterPerson quota full for today.), 422, {'Content-Type' => 'application/xml'} - mock.post "/people.json", {}, %q({"errors":["Age can't be blank","Name can't be blank","Name must start with a letter","Person quota full for today."]}), 422, {'Content-Type' => 'application/json'} + mock.post "/people.xml", {}, %q(Age can't be blankName can't be blankName must start with a letterPerson quota full for today.), 422, {'Content-Type' => 'application/xml; charset=utf-8'} + mock.post "/people.json", {}, %q({"errors":["Age can't be blank","Name can't be blank","Name must start with a letter","Person quota full for today."]}), 422, {'Content-Type' => 'application/json; charset=utf-8'} end @person = Person.new(:name => '', :age => '') assert_equal @person.save, false -- 1.6.4.2 From e18752868aec20c74f87dcb4ee64575a405c0f78 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 17 Sep 2009 11:39:14 -0700 Subject: [PATCH 319/779] making nokogiri to hash less clever, more fast O_o [#2243 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/active_support/xml_mini/nokogiri.rb | 32 +++++++------------ 1 files changed, 12 insertions(+), 20 deletions(-) diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index a77a7f9..a354ad3 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -12,7 +12,7 @@ module ActiveSupport if string.blank? {} else - doc = Nokogiri::XML(string) + doc = Nokogiri::XML(data) { |cfg| cfg.noblanks } raise doc.errors.first if doc.errors.length > 0 doc.to_hash end @@ -33,33 +33,25 @@ module ActiveSupport # hash:: # Hash to merge the converted element into. def to_hash(hash = {}) - hash[name] ||= attributes_as_hash + attributes = attributes_as_hash + if hash[name] + hash[name] = [hash[name]].flatten + hash[name] << attributes + else + hash[name] ||= attributes + end - walker = lambda { |memo, parent, child, callback| - next if child.blank? && 'file' != parent['type'] + children.each { |child| + next if child.blank? && 'file' != self['type'] if child.text? || child.cdata? - (memo[CONTENT_ROOT] ||= '') << child.content + (attributes[CONTENT_ROOT] ||= '') << child.content next end - name = child.name - - child_hash = child.attributes_as_hash - if memo[name] - memo[name] = [memo[name]].flatten - memo[name] << child_hash - else - memo[name] = child_hash - end - - # Recusively walk children - child.children.each { |c| - callback.call(child_hash, child, c, callback) - } + child.to_hash attributes } - children.each { |c| walker.call(hash[name], self, c, walker) } hash end -- 1.6.4.2 From 6222ac1a91e5beb44d875ec9c0072bbac6f3d04d Mon Sep 17 00:00:00 2001 From: Michael Gunderloy Date: Mon, 21 Sep 2009 17:50:14 -0500 Subject: [PATCH 320/779] Fix variable error in Nokogiri XmlMini code [#3242 state:resolved] Signed-off-by: Pratik Naik --- .../lib/active_support/xml_mini/nokogiri.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index a354ad3..f3c64c6 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -12,7 +12,7 @@ module ActiveSupport if string.blank? {} else - doc = Nokogiri::XML(data) { |cfg| cfg.noblanks } + doc = Nokogiri::XML(string) { |cfg| cfg.noblanks } raise doc.errors.first if doc.errors.length > 0 doc.to_hash end -- 1.6.4.2 From c680f2372e2d4081597bb130715ee79daaf80576 Mon Sep 17 00:00:00 2001 From: Manfred Stienstra Date: Mon, 21 Sep 2009 14:24:50 +0200 Subject: [PATCH 321/779] Prefix Internet Explorer's accepted mime types with sensible defaults. --- actionpack/lib/action_controller/request.rb | 15 ++++++++-- actionpack/test/controller/request_test.rb | 37 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 1c3c1c8..a5fe383 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -99,7 +99,15 @@ module ActionController content_type.to_s end - # Returns the accepted MIME type for the request. + # Regular expression to match the Internet Explorer user agent string. + INTERNET_EXPLORER = /MSIE\s[\d\.]+;/ + + # Returns true if the user agent is Internet Explorer. + def internet_explorer? + user_agent =~ INTERNET_EXPLORER + end + + # Returns the accepted MIME types for the request. def accepts @accepts ||= begin header = @env['HTTP_ACCEPT'].to_s.strip @@ -107,7 +115,9 @@ module ActionController if header.empty? [content_type, Mime::ALL].compact else - Mime::Type.parse(header) + accepts = Mime::Type.parse(header) + accepts.unshift(Mime::HTML, Mime::XML) if internet_explorer? + accepts end end end @@ -164,7 +174,6 @@ module ActionController end end - # Sets the \format by string extension, which can be used to force custom formats # that are not controlled by the extension. # diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/controller/request_test.rb index c4cc63e..b41224a 100644 --- a/actionpack/test/controller/request_test.rb +++ b/actionpack/test/controller/request_test.rb @@ -371,6 +371,43 @@ class RequestTest < ActiveSupport::TestCase assert_equal Mime::XML, request.content_type end + def test_internet_explorer_user_agent_check + assert([ + "curl 7.16.1 (i386-portbld-freebsd6.2) libcurl/7.16.1 OpenSSL/0.9.7m zlib/1.2.3", + "Opera/9.80 (Macintosh; Intel Mac OS X; U; en) Presto/2.2.15 Version/10.00", + "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-us) AppleWebKit/531.9 (KHTML, like Gecko) Version/4.0.3 Safari/531.9", + "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.23) Gecko/20090815 Camino/1.6.9 (like Firefox/2.0.0.23)", + "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5" + ].all? do |user_agent| + !stub_request('HTTP_USER_AGENT' => user_agent).internet_explorer? + end) + + assert([ + "Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)", + "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; )", + "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Win64; x64; Trident/4.0)" + ].all? do |user_agent| + stub_request('HTTP_USER_AGENT' => user_agent).internet_explorer? + end) + end + + def test_accepts_without_user_agent + request = stub_request 'HTTP_ACCEPT' => 'text/html, text/csv, */*' + assert_equal [Mime::HTML, Mime::CSV, Mime::ALL], request.accepts + end + + def test_accepts_for_regular_user_agent + request = stub_request 'HTTP_USER_AGENT' => "curl 7.16.1 (i386-portbld-freebsd6.2) libcurl/7.16.1 OpenSSL/0.9.7m zlib/1.2.3", 'HTTP_ACCEPT' => 'text/html, text/csv, */*' + assert_equal [Mime::HTML, Mime::CSV, Mime::ALL], request.accepts + end + + def test_accepts_for_internet_explorer + request = stub_request 'HTTP_USER_AGENT' => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Win64; x64)', + 'HTTP_ACCEPT' => "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/x-shockwave-flash, application/x-silverlight, */*" + assert_equal 16, request.accepts.length + assert_equal [Mime::HTML, Mime::XML], request.accepts[0..1] + end + def test_user_agent request = stub_request 'HTTP_USER_AGENT' => 'TestAgent' assert_equal 'TestAgent', request.user_agent -- 1.6.4.2 From cddd4746f9a2fba8215d53a0f9ac5241f128d07b Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Fri, 25 Sep 2009 15:51:27 +0200 Subject: [PATCH 322/779] Rewrote ActionView::TestCase. The test case now mimicks the template environment more closely, so it's possible to use render, load helper dependencies. This also fixes assert_select, and similar assertions. Because view tests and helpers generally don't render full templates assert_select looks first in rendered and then in output_buffer to find the rendered output. --- actionpack/lib/action_controller/test_process.rb | 2 +- actionpack/lib/action_view/test_case.rb | 128 ++++++++++++--- actionpack/test/fixtures/test/_from_helper.erb | 1 + .../test/template/active_record_helper_test.rb | 2 +- actionpack/test/template/benchmark_helper_test.rb | 12 +- actionpack/test/view/test_case_test.rb | 173 +++++++++++++++++++- 6 files changed, 278 insertions(+), 40 deletions(-) create mode 100644 actionpack/test/fixtures/test/_from_helper.erb diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 9de3fab..fd9f90b 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -91,7 +91,7 @@ module ActionController #:nodoc: @path || super() end - def assign_parameters(controller_path, action, parameters) + def assign_parameters(controller_path, action, parameters = {}) parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action) extra_keys = ActionController::Routing::Routes.extra_keys(parameters) non_path_parameters = get? ? query_parameters : request_parameters diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index ec337bb..eef5ba8 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -22,11 +22,52 @@ module ActionView end class TestCase < ActiveSupport::TestCase + class TestController < ActionController::Base + attr_accessor :request, :response, :params + + def self.controller_path + '' + end + + def initialize + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + + @params = {} + send(:initialize_current_url) + end + end + include ActionController::TestCase::Assertions include ActionController::TestProcess + include ActionController::PolymorphicRoutes + include ActionController::RecordIdentifier + + include ActionView::Helpers + include ActionController::Helpers + class_inheritable_accessor :helper_class - @@helper_class = nil + attr_accessor :controller, :output_buffer, :rendered + + setup :setup_with_controller + def setup_with_controller + @controller = TestController.new + @output_buffer = '' + @rendered = '' + + self.class.send(:include_helper_modules!) + make_test_case_available_to_view! + end + + def render(options = {}, local_assigns = {}, &block) + @rendered << output = _view.render(options, local_assigns, &block) + output + end + + def protect_against_forgery? + false + end class << self def tests(helper_class) @@ -46,42 +87,75 @@ module ActionView rescue NameError nil end - end - include ActionView::Helpers - include ActionController::PolymorphicRoutes - include ActionController::RecordIdentifier + def helper_method(*methods) + # Almost a duplicate from ActionController::Helpers + methods.flatten.each do |method| + master_helper_module.module_eval <<-end_eval + def #{method}(*args, &block) # def current_user(*args, &block) + _test_case.send(%(#{method}), *args, &block) # test_case.send(%(current_user), *args, &block) + end # end + end_eval + end + end - setup :setup_with_helper_class + private + def include_helper_modules! + helper(helper_class) if helper_class + include master_helper_module + end + end - def setup_with_helper_class - if helper_class && !self.class.ancestors.include?(helper_class) - self.class.send(:include, helper_class) + private + def make_test_case_available_to_view! + test_case_instance = self + master_helper_module.module_eval do + define_method(:_test_case) { test_case_instance } + private :_test_case + end end - self.output_buffer = '' - end + def _view + view = ActionView::Base.new(ActionController::Base.view_paths, _assigns, @controller) + view.helpers.include master_helper_module + view + end - class TestController < ActionController::Base - attr_accessor :request, :response, :params + # Support the selector assertions + # + # Need to experiment if this priority is the best one: rendered => output_buffer + def response_from_page_or_rjs + HTML::Document.new(rendered.blank? ? output_buffer : rendered).root + end - def initialize - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - - @params = {} - send(:initialize_current_url) + EXCLUDE_IVARS = %w{ + @output_buffer + @fixture_cache + @method_name + @_result + @loaded_fixtures + @test_passed + @view + } + + def _instance_variables + instance_variables - EXCLUDE_IVARS end - end - protected - attr_accessor :output_buffer + def _assigns + _instance_variables.inject({}) do |hash, var| + name = var[1..-1].to_sym + hash[name] = instance_variable_get(var) + hash + end + end - private def method_missing(selector, *args) - controller = TestController.new - return controller.__send__(selector, *args) if ActionController::Routing::Routes.named_routes.helpers.include?(selector) - super + if ActionController::Routing::Routes.named_routes.helpers.include?(selector) + @controller.__send__(selector, *args) + else + super + end end end -end +end \ No newline at end of file diff --git a/actionpack/test/fixtures/test/_from_helper.erb b/actionpack/test/fixtures/test/_from_helper.erb new file mode 100644 index 0000000..16de7c0 --- /dev/null +++ b/actionpack/test/fixtures/test/_from_helper.erb @@ -0,0 +1 @@ +<%= render_from_helper %> \ No newline at end of file diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index 11812e7..6ec4c84 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -183,7 +183,7 @@ class ActiveRecordHelperTest < ActionView::TestCase end def test_form_with_action_option - @response.body = form("post", :action => "sign") + output_buffer << form("post", :action => "sign") assert_select "form[action=sign]" do |form| assert_select "input[type=submit][value=Sign]" end diff --git a/actionpack/test/template/benchmark_helper_test.rb b/actionpack/test/template/benchmark_helper_test.rb index 5d2af7c..ac31fc6 100644 --- a/actionpack/test/template/benchmark_helper_test.rb +++ b/actionpack/test/template/benchmark_helper_test.rb @@ -4,14 +4,14 @@ require 'action_view/helpers/benchmark_helper' class BenchmarkHelperTest < ActionView::TestCase tests ActionView::Helpers::BenchmarkHelper - def teardown - controller.logger.send(:clear_buffer) + def setup + super + controller.logger = ActiveSupport::BufferedLogger.new(StringIO.new) + controller.logger.auto_flushing = false end - def controller - logger = ActiveSupport::BufferedLogger.new(StringIO.new) - logger.auto_flushing = false - @controller ||= Struct.new(:logger).new(logger) + def teardown + controller.logger.send(:clear_buffer) end def test_without_block diff --git a/actionpack/test/view/test_case_test.rb b/actionpack/test/view/test_case_test.rb index 9124198..2b7aa81 100644 --- a/actionpack/test/view/test_case_test.rb +++ b/actionpack/test/view/test_case_test.rb @@ -1,8 +1,171 @@ require 'abstract_unit' -class TestCaseTest < ActionView::TestCase - def test_should_have_current_url - controller = TestController.new - assert_nothing_raised(NoMethodError){ controller.url_for({:controller => "foo", :action => "index"}) } +module ActionView + class TestCase + module ATestHelper + end + + module AnotherTestHelper + def from_another_helper + 'Howdy!' + end + end + + module ASharedTestHelper + def from_shared_helper + 'Holla!' + end + end + helper ASharedTestHelper + + module SharedTests + def self.included(test_case) + test_case.class_eval do + test "helpers defined on ActionView::TestCase are available" do + assert test_case.ancestors.include?(ASharedTestHelper) + assert 'Holla!', from_shared_helper + end + end + end + end + + class GeneralViewTest < ActionView::TestCase + include SharedTests + test_case = self + + test "works without testing a helper module" do + assert_equal 'Eloy', render('developers/developer', :developer => stub(:name => 'Eloy')) + end + + helper AnotherTestHelper + test "additional helper classes can be specified as in a controller" do + assert test_case.ancestors.include?(AnotherTestHelper) + assert 'Howdy!', from_another_helper + end + end + + class ClassMethodsTest < ActionView::TestCase + include SharedTests + test_case = self + + tests ATestHelper + test "tests the specified helper module" do + assert_equal ATestHelper, test_case.helper_class + assert test_case.ancestors.include?(ATestHelper) + end + + helper AnotherTestHelper + test "additional helper classes can be specified as in a controller" do + assert test_case.ancestors.include?(AnotherTestHelper) + assert 'Howdy!', from_another_helper + + test_case.helper_class.module_eval do + def render_from_helper + from_another_helper + end + end + assert 'Howdy!', render(:partial => 'test/from_helper') + end + end + + class ATestHelperTest < ActionView::TestCase + include SharedTests + test_case = self + + test "inflects the name of the helper module to test from the test case class" do + assert_equal ATestHelper, test_case.helper_class + assert test_case.ancestors.include?(ATestHelper) + end + + test "a configured test controller is available" do + assert_kind_of ActionController::Base, controller + assert_equal '', controller.controller_path + end + + test "helper class that is being tested is always included in view instance" do + self.class.helper_class.module_eval do + def render_from_helper + render :partial => 'customer', :collection => @customers + end + end + + TestController.stubs(:controller_path).returns('test') + + @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')] + assert_match /Hello: EloyHello: Manfred/, render(:partial => 'test/from_helper') + end + + test "no additional helpers should shared across test cases" do + assert !test_case.ancestors.include?(AnotherTestHelper) + assert_raise(NoMethodError) { send :from_another_helper } + end + + test "is able to use routes" do + controller.request.assign_parameters('foo', 'index') + assert_equal '/foo', url_for + assert_equal '/bar', url_for(:controller => 'bar') + end + + test "is able to use named routes" do + with_routing do |set| + set.draw { |map| map.resources :contents } + assert_equal 'http://test.host/contents/new', new_content_url + assert_equal 'http://test.host/contents/1', content_url(:id => 1) + end + end + + test "named routes can be used from helper included in view" do + with_routing do |set| + set.draw { |map| map.resources :contents } + master_helper_module.module_eval do + def render_from_helper + new_content_url + end + end + + assert_equal 'http://test.host/contents/new', render(:partial => 'test/from_helper') + end + end + + test "is able to render partials with local variables" do + assert_equal 'Eloy', render('developers/developer', :developer => stub(:name => 'Eloy')) + assert_equal 'Eloy', render(:partial => 'developers/developer', + :locals => { :developer => stub(:name => 'Eloy') }) + end + + test "is able to render partials from templates and also use instance variables" do + TestController.stubs(:controller_path).returns('test') + + @customers = [stub(:name => 'Eloy'), stub(:name => 'Manfred')] + assert_match /Hello: EloyHello: Manfred/, render(:file => 'test/list') + end + + test "is able to make methods available to the view" do + master_helper_module.module_eval do + def render_from_helper; from_test_case end + end + assert_equal 'Word!', render(:partial => 'test/from_helper') + end + + def from_test_case; 'Word!'; end + helper_method :from_test_case + end + + class AssertionsTest < ActionView::TestCase + def render_from_helper + form_tag('/foo') do + concat render(:text => '
  • foo
') + end + end + helper_method :render_from_helper + + test "uses the output_buffer for assert_select" do + render(:partial => 'test/from_helper') + + assert_select 'form' do + assert_select 'li', :text => 'foo' + end + end + end end -end +end \ No newline at end of file -- 1.6.4.2 From deac481eb747a6c50e27ef22d0d3c8e0e9a6f206 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Fri, 25 Sep 2009 17:05:30 +0200 Subject: [PATCH 323/779] Made assert_dom_equal and assert_dom_not_equal ignore meaningless whitespace. Also changed message of assert_dom_equal to be like assert_equal. --- .../action_controller/assertions/dom_assertions.rb | 21 ++++++- actionpack/test/controller/dom_assertions_test.rb | 62 ++++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 actionpack/test/controller/dom_assertions_test.rb diff --git a/actionpack/lib/action_controller/assertions/dom_assertions.rb b/actionpack/lib/action_controller/assertions/dom_assertions.rb index 5ffe5f1..6baad5e 100644 --- a/actionpack/lib/action_controller/assertions/dom_assertions.rb +++ b/actionpack/lib/action_controller/assertions/dom_assertions.rb @@ -1,6 +1,17 @@ module ActionController module Assertions module DomAssertions + def self.strip_whitespace!(nodes) + nodes.reject! do |node| + if node.is_a?(HTML::Text) + node.to_s.strip.empty? + else + strip_whitespace! node.children + false + end + end + end + # Test two HTML strings for equivalency (e.g., identical up to reordering of attributes) # # ==== Examples @@ -12,13 +23,15 @@ module ActionController clean_backtrace do expected_dom = HTML::Document.new(expected).root actual_dom = HTML::Document.new(actual).root - full_message = build_message(message, " expected to be == to\n.", expected_dom.to_s, actual_dom.to_s) + DomAssertions.strip_whitespace!(expected_dom.children) + DomAssertions.strip_whitespace!(actual_dom.children) + full_message = build_message(message, " expected but was\n.", expected_dom.to_s, actual_dom.to_s) assert_block(full_message) { expected_dom == actual_dom } end end - # The negated form of +assert_dom_equivalent+. + # The negated form of +assert_dom_equal+. # # ==== Examples # @@ -29,8 +42,10 @@ module ActionController clean_backtrace do expected_dom = HTML::Document.new(expected).root actual_dom = HTML::Document.new(actual).root - full_message = build_message(message, " expected to be != to\n.", expected_dom.to_s, actual_dom.to_s) + DomAssertions.strip_whitespace!(expected_dom.children) + DomAssertions.strip_whitespace!(actual_dom.children) + full_message = build_message(message, " expected to be != to\n.", expected_dom.to_s, actual_dom.to_s) assert_block(full_message) { expected_dom != actual_dom } end end diff --git a/actionpack/test/controller/dom_assertions_test.rb b/actionpack/test/controller/dom_assertions_test.rb new file mode 100644 index 0000000..831a4c3 --- /dev/null +++ b/actionpack/test/controller/dom_assertions_test.rb @@ -0,0 +1,62 @@ +require 'abstract_unit' + +class DomAssertionsTest < ActionView::TestCase + def setup + super + @html_only = '
  • foo
  • bar
' + @html_with_meaningless_whitespace = %{ +
    +
  • foo
  • +
  • bar
  • +
+ } + @more_html_with_meaningless_whitespace = %{
    + +
  • foo
  • + +
  • bar
} + @html_with_meaningful_whitespace = '
  • foo
  • bar
' + end + + test "assert_dom_equal strips meaningless whitespace from expected string" do + assert_dom_equal @html_with_meaningless_whitespace, @html_only + end + + test "assert_dom_equal strips meaningless whitespace from actual string" do + assert_dom_equal @html_only, @html_with_meaningless_whitespace + end + + test "assert_dom_equal strips meaningless whitespace from both expected and actual strings" do + assert_dom_equal @more_html_with_meaningless_whitespace, @html_with_meaningless_whitespace + end + + test "assert_dom_equal does not strip important whitespace" do + assert_assertion_fails do + assert_dom_equal @html_with_meaningful_whitespace, @html_only + end + end + + test "assert_dom_not_equal strips meaningless whitespace from expected string" do + assert_assertion_fails { assert_dom_not_equal @html_with_meaningless_whitespace, @html_only } + end + + test "assert_dom_not_equal strips meaningless whitespace from actual string" do + assert_assertion_fails { assert_dom_not_equal @html_only, @html_with_meaningless_whitespace } + end + + test "assert_dom_not_equal strips meaningless whitespace from both expected and actual strings" do + assert_assertion_fails do + assert_dom_not_equal @more_html_with_meaningless_whitespace, @html_with_meaningless_whitespace + end + end + + test "assert_dom_not_equal does not strip important whitespace" do + assert_dom_not_equal @html_with_meaningful_whitespace, @html_only + end + + private + + def assert_assertion_fails + assert_raise(Test::Unit::AssertionFailedError) { yield } + end +end \ No newline at end of file -- 1.6.4.2 From b18248ff05629da37ff7752a48878a486aaf72fc Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Fri, 25 Sep 2009 17:46:21 +0200 Subject: [PATCH 324/779] The DomAssertions now also strip surrounding whitespace inside tags. --- .../action_controller/assertions/dom_assertions.rb | 3 ++- actionpack/test/controller/dom_assertions_test.rb | 17 ++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/action_controller/assertions/dom_assertions.rb b/actionpack/lib/action_controller/assertions/dom_assertions.rb index 6baad5e..8dd3f63 100644 --- a/actionpack/lib/action_controller/assertions/dom_assertions.rb +++ b/actionpack/lib/action_controller/assertions/dom_assertions.rb @@ -4,7 +4,8 @@ module ActionController def self.strip_whitespace!(nodes) nodes.reject! do |node| if node.is_a?(HTML::Text) - node.to_s.strip.empty? + node.content.strip! + node.content.empty? else strip_whitespace! node.children false diff --git a/actionpack/test/controller/dom_assertions_test.rb b/actionpack/test/controller/dom_assertions_test.rb index 831a4c3..11bf8d8 100644 --- a/actionpack/test/controller/dom_assertions_test.rb +++ b/actionpack/test/controller/dom_assertions_test.rb @@ -6,8 +6,10 @@ class DomAssertionsTest < ActionView::TestCase @html_only = '
  • foo
  • bar
' @html_with_meaningless_whitespace = %{
    -
  • foo
  • -
  • bar
  • +
  • \tfoo
  • +
  • + bar +
} @more_html_with_meaningless_whitespace = %{
    @@ -15,7 +17,6 @@ class DomAssertionsTest < ActionView::TestCase
  • foo
  • bar
} - @html_with_meaningful_whitespace = '
  • foo
  • bar
' end test "assert_dom_equal strips meaningless whitespace from expected string" do @@ -30,12 +31,6 @@ class DomAssertionsTest < ActionView::TestCase assert_dom_equal @more_html_with_meaningless_whitespace, @html_with_meaningless_whitespace end - test "assert_dom_equal does not strip important whitespace" do - assert_assertion_fails do - assert_dom_equal @html_with_meaningful_whitespace, @html_only - end - end - test "assert_dom_not_equal strips meaningless whitespace from expected string" do assert_assertion_fails { assert_dom_not_equal @html_with_meaningless_whitespace, @html_only } end @@ -50,10 +45,6 @@ class DomAssertionsTest < ActionView::TestCase end end - test "assert_dom_not_equal does not strip important whitespace" do - assert_dom_not_equal @html_with_meaningful_whitespace, @html_only - end - private def assert_assertion_fails -- 1.6.4.2 From 4a11ca1c7ebe7a45fd01de7e73ebe6c46c1e4bfa Mon Sep 17 00:00:00 2001 From: Chad Woolley Date: Sat, 26 Sep 2009 12:23:18 -0500 Subject: [PATCH 325/779] include nokogiri gem, so activesupport nokogiri tests run Signed-off-by: Joshua Peek --- ci/geminstaller.yml | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml index 39e3fc9..1bf754c 100644 --- a/ci/geminstaller.yml +++ b/ci/geminstaller.yml @@ -11,6 +11,8 @@ gems: - name: mysql #version: >= 2.7 version: = 2.7 +- name: nokogiri + version: >= 1.3.3 - name: pg version: >= 0.8.0 - name: rack -- 1.6.4.2 From 8371d6f0c1948d3201c1db1fea4b1604a238d48d Mon Sep 17 00:00:00 2001 From: Gaspard Bucher Date: Tue, 15 Sep 2009 09:56:37 +0200 Subject: [PATCH 326/779] Fixes a bug where layouts provided with an absolute path would not be found because they were prefixed by 'layouts'. This bug only appears if the path does not contain the word 'layouts'. Signed-off-by: Michael Koziarski [#3207 state:committed] --- actionpack/lib/action_controller/layout.rb | 2 +- actionpack/test/controller/layout_test.rb | 11 +++++++++++ .../fixtures/layout_tests/abs_path_layout.rhtml | 1 + 3 files changed, 13 insertions(+), 1 deletions(-) create mode 100644 actionpack/test/fixtures/layout_tests/abs_path_layout.rhtml diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb index 6ec0c1b..c39e1fa 100644 --- a/actionpack/lib/action_controller/layout.rb +++ b/actionpack/lib/action_controller/layout.rb @@ -221,7 +221,7 @@ module ActionController #:nodoc: end def find_layout(layout, format, html_fallback=false) #:nodoc: - view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", format, html_fallback) + view_paths.find_template(layout.to_s =~ /\A\/|layouts\// ? layout : "layouts/#{layout}", format, html_fallback) rescue ActionView::MissingTemplate raise if Mime::Type.lookup_by_extension(format.to_s).html? end diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index 1575674..7bed963 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -83,6 +83,11 @@ class AbsolutePathLayoutController < LayoutTest layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test.rhtml') end +class AbsolutePathWithoutLayoutsController < LayoutTest + # Absolute layout path without 'layouts' in it. + layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/abs_path_layout.rhtml') +end + class HasOwnLayoutController < LayoutTest layout 'item' end @@ -153,6 +158,12 @@ class LayoutSetInResponseTest < ActionController::TestCase get :hello assert_equal "layout_test.rhtml hello.rhtml", @response.body.strip end + + def test_absolute_pathed_layout_without_layouts_in_path + @controller = AbsolutePathWithoutLayoutsController.new + get :hello + assert_equal "abs_path_layout.rhtml hello.rhtml", @response.body.strip + end end class RenderWithTemplateOptionController < LayoutTest diff --git a/actionpack/test/fixtures/layout_tests/abs_path_layout.rhtml b/actionpack/test/fixtures/layout_tests/abs_path_layout.rhtml new file mode 100644 index 0000000..8ba6e70 --- /dev/null +++ b/actionpack/test/fixtures/layout_tests/abs_path_layout.rhtml @@ -0,0 +1 @@ +abs_path_layout.rhtml <%= yield %> \ No newline at end of file -- 1.6.4.2 From 14a6794a8ef9995c45a86e95c45d315c61087103 Mon Sep 17 00:00:00 2001 From: Luciano G Panaro Date: Sat, 26 Sep 2009 10:33:50 -0300 Subject: [PATCH 327/779] Make has_one with :conditions hash scope build or creation of the associated object with those conditions Signed-off-by: Michael Koziarski [#3088 state:committed] --- activerecord/lib/active_record/associations.rb | 4 +++- .../associations/has_one_association.rb | 9 +++++++++ .../associations/has_one_associations_test.rb | 18 ++++++++++++++++++ activerecord/test/cases/reflection_test.rb | 4 ++-- activerecord/test/models/company.rb | 2 ++ 5 files changed, 34 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index c739fdd..fab6359 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -875,7 +875,9 @@ module ActiveRecord # if the real class name is Person, you'll have to specify it with this option. # [:conditions] # Specify the conditions that the associated object must meet in order to be included as a +WHERE+ - # SQL fragment, such as rank = 5. + # SQL fragment, such as rank = 5. Record creation from the association is scoped if a hash + # is used. has_one :account, :conditions => {:enabled => true} will create an enabled account with @company.create_account + # or @company.build_account. # [:order] # Specify the order in which the associated objects are returned as an ORDER BY SQL fragment, # such as last_name, first_name DESC. diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index b92cbbd..b0105d5 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -8,18 +8,21 @@ module ActiveRecord def create(attrs = {}, replace_existing = true) new_record(replace_existing) do |reflection| + attrs = merge_with_conditions(attrs) reflection.create_association(attrs) end end def create!(attrs = {}, replace_existing = true) new_record(replace_existing) do |reflection| + attrs = merge_with_conditions(attrs) reflection.create_association!(attrs) end end def build(attrs = {}, replace_existing = true) new_record(replace_existing) do |reflection| + attrs = merge_with_conditions(attrs) reflection.build_association(attrs) end end @@ -119,6 +122,12 @@ module ActiveRecord record end + + def merge_with_conditions(attrs={}) + attrs ||= {} + attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash) + attrs + end end end end diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index aceda3f..bb9189f 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -315,4 +315,22 @@ class HasOneAssociationsTest < ActiveRecord::TestCase Firm.find(@firm.id, :include => :account).save! end end + + def test_build_respects_hash_condition + account = companies(:first_firm).build_account_limit_500_with_hash_conditions + assert account.save + assert_equal 500, account.credit_limit + end + + def test_create_respects_hash_condition + account = companies(:first_firm).create_account_limit_500_with_hash_conditions + assert !account.new_record? + assert_equal 500, account.credit_limit + end + + def test_create!_respects_hash_condition + account = companies(:first_firm).create_account_limit_500_with_hash_conditions! + assert !account.new_record? + assert_equal 500, account.credit_limit + end end diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 78bcc41..b42adf1 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -170,9 +170,9 @@ class ReflectionTest < ActiveRecord::TestCase def test_reflection_of_all_associations # FIXME these assertions bust a lot - assert_equal 34, Firm.reflect_on_all_associations.size + assert_equal 35, Firm.reflect_on_all_associations.size assert_equal 25, Firm.reflect_on_all_associations(:has_many).size - assert_equal 9, Firm.reflect_on_all_associations(:has_one).size + assert_equal 10, Firm.reflect_on_all_associations(:has_one).size assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 1a4f777..a45eb3b 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -75,6 +75,8 @@ class Firm < Company has_one :account_using_foreign_and_primary_keys, :foreign_key => "firm_name", :primary_key => "name", :class_name => "Account" has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete + has_one :account_limit_500_with_hash_conditions, :foreign_key => "firm_id", :class_name => "Account", :conditions => { :credit_limit => 500 } + has_one :unautosaved_account, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false has_many :accounts has_many :unautosaved_accounts, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false -- 1.6.4.2 From b372b4c87515ab84e64028b437da1e047a128e4d Mon Sep 17 00:00:00 2001 From: Jay Pignata Date: Fri, 4 Sep 2009 12:43:17 -0400 Subject: [PATCH 328/779] Enhancing distance_of_time_in_words to prefix year output with over and about depending upon how many months have elapsed Signed-off-by: Michael Koziarski [#3106 state:committed] --- actionpack/lib/action_view/helpers/date_helper.rb | 11 +++++++---- actionpack/test/template/date_helper_test.rb | 15 ++++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index c74909a..b0a027e 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -43,13 +43,14 @@ module ActionView # distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour # distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute # distance_of_time_in_words(from_time, from_time + 15.seconds, true) # => less than 20 seconds - # distance_of_time_in_words(from_time, 3.years.from_now) # => over 3 years + # distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years # distance_of_time_in_words(from_time, from_time + 60.hours) # => about 3 days # distance_of_time_in_words(from_time, from_time + 45.seconds, true) # => less than a minute # distance_of_time_in_words(from_time, from_time - 45.seconds, true) # => less than a minute # distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute # distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year - # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => over 4 years + # distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years + # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years # # to_time = Time.now + 6.years + 19.days # distance_of_time_in_words(from_time, to_time, true) # => over 6 years @@ -85,8 +86,10 @@ module ActionView when 2880..43199 then locale.t :x_days, :count => (distance_in_minutes / 1440).round when 43200..86399 then locale.t :about_x_months, :count => 1 when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes / 43200).round - when 525600..1051199 then locale.t :about_x_years, :count => 1 - else locale.t :over_x_years, :count => (distance_in_minutes / 525600).round + else + return distance_in_minutes % 525600 < 262800 ? + locale.t(:about_x_years, :count => (distance_in_minutes / 525600).round) : + locale.t(:over_x_years, :count => (distance_in_minutes / 525600).round) end end end diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index 2e4763f..51a340d 100644 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -69,13 +69,14 @@ class DateHelperTest < ActionView::TestCase assert_equal "2 months", distance_of_time_in_words(from, to + 59.days + 23.hours + 59.minutes + 30.seconds) assert_equal "12 months", distance_of_time_in_words(from, to + 1.years - 31.seconds) - # 525600..1051199 + # > 525599 assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years - 30.seconds) - assert_equal "about 1 year", distance_of_time_in_words(from, to + 2.years - 31.seconds) - - # > 1051199 - assert_equal "over 2 years", distance_of_time_in_words(from, to + 2.years + 30.seconds) - assert_equal "over 10 years", distance_of_time_in_words(from, to + 10.years) + assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years + 6.months - 1.day) + assert_equal "over 1 year", distance_of_time_in_words(from, to + 1.years + 6.months) + assert_equal "about 2 years", distance_of_time_in_words(from, to + 2.years + 30.seconds) + assert_equal "about 5 years", distance_of_time_in_words(from, to + 5.years + 5.months) + assert_equal "over 5 years", distance_of_time_in_words(from, to + 5.years + 6.months) + assert_equal "about 10 years", distance_of_time_in_words(from, to + 10.years) # test to < from assert_equal "about 4 hours", distance_of_time_in_words(from + 4.hours, to) @@ -104,7 +105,7 @@ class DateHelperTest < ActionView::TestCase def test_distance_in_words_with_dates start_date = Date.new 1975, 1, 31 end_date = Date.new 1977, 1, 31 - assert_equal("over 2 years", distance_of_time_in_words(start_date, end_date)) + assert_equal("about 2 years", distance_of_time_in_words(start_date, end_date)) end def test_distance_in_words_with_integers -- 1.6.4.2 From f489b3341c6f5488eadeea0544ce0decc22e351e Mon Sep 17 00:00:00 2001 From: John Trupiano Date: Sat, 26 Sep 2009 15:42:18 -0400 Subject: [PATCH 329/779] Introduce :almost keyword for distance_of_time_in_words. Make 1.75 days - 2 days return '2 days'. Signed-off-by: Michael Koziarski [#3266 state:committed] --- actionpack/lib/action_view/helpers/date_helper.rb | 29 ++++++++++----- actionpack/lib/action_view/locale/en.yml | 3 ++ actionpack/test/template/date_helper_i18n_test.rb | 19 +++++----- actionpack/test/template/date_helper_test.rb | 39 ++++++++++++++------ 4 files changed, 59 insertions(+), 31 deletions(-) diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index b0a027e..4ba48e7 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -26,8 +26,10 @@ module ActionView # 47 hrs, 59 mins, 29 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days # 29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 1 month # 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months - # 1 yr <-> 2 yrs minus 1 secs # => about 1 year - # 2 yrs <-> max time or date # => over [2..X] years + # 1 yr <-> 1 yr, 3 months # => about 1 year + # 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year + # 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years + # 2 yrs <-> max time or date # => (same rules as 1 yr) # # With include_seconds = true and the difference < 1 minute 29 seconds: # 0-4 secs # => less than 5 seconds @@ -53,8 +55,8 @@ module ActionView # distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years # # to_time = Time.now + 6.years + 19.days - # distance_of_time_in_words(from_time, to_time, true) # => over 6 years - # distance_of_time_in_words(to_time, from_time, true) # => over 6 years + # distance_of_time_in_words(from_time, to_time, true) # => about 6 years + # distance_of_time_in_words(to_time, from_time, true) # => about 6 years # distance_of_time_in_words(Time.now, Time.now) # => less than a minute # def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {}) @@ -82,14 +84,21 @@ module ActionView when 2..44 then locale.t :x_minutes, :count => distance_in_minutes when 45..89 then locale.t :about_x_hours, :count => 1 when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round - when 1440..2879 then locale.t :x_days, :count => 1 - when 2880..43199 then locale.t :x_days, :count => (distance_in_minutes / 1440).round + when 1440..2529 then locale.t :x_days, :count => 1 + when 2530..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round when 43200..86399 then locale.t :about_x_months, :count => 1 - when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes / 43200).round + when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round else - return distance_in_minutes % 525600 < 262800 ? - locale.t(:about_x_years, :count => (distance_in_minutes / 525600).round) : - locale.t(:over_x_years, :count => (distance_in_minutes / 525600).round) + distance_in_years = distance_in_minutes / 525600 + minute_offset_for_leap_year = (distance_in_years / 4) * 1440 + remainder = ((distance_in_minutes - minute_offset_for_leap_year) % 525600) + if remainder < 131400 + locale.t(:about_x_years, :count => distance_in_years) + elsif remainder < 394200 + locale.t(:over_x_years, :count => distance_in_years) + else + locale.t(:almost_x_years, :count => distance_in_years + 1) + end end end end diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml index c82cd07..84d94fd 100644 --- a/actionpack/lib/action_view/locale/en.yml +++ b/actionpack/lib/action_view/locale/en.yml @@ -91,6 +91,9 @@ over_x_years: one: "over 1 year" other: "over {{count}} years" + almost_x_years: + one: "almost 1 year" + other: "almost {{count}} years" prompts: year: "Year" month: "Month" diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb index bc011f5..b69a449 100644 --- a/actionpack/test/template/date_helper_i18n_test.rb +++ b/actionpack/test/template/date_helper_i18n_test.rb @@ -20,15 +20,16 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase [60.seconds, true] => [:'x_minutes', 1], # without include_seconds - [29.seconds, false] => [:'less_than_x_minutes', 1], - [60.seconds, false] => [:'x_minutes', 1], - [44.minutes, false] => [:'x_minutes', 44], - [61.minutes, false] => [:'about_x_hours', 1], - [24.hours, false] => [:'x_days', 1], - [30.days, false] => [:'about_x_months', 1], - [60.days, false] => [:'x_months', 2], - [1.year, false] => [:'about_x_years', 1], - [3.years, false] => [:'over_x_years', 3] + [29.seconds, false] => [:'less_than_x_minutes', 1], + [60.seconds, false] => [:'x_minutes', 1], + [44.minutes, false] => [:'x_minutes', 44], + [61.minutes, false] => [:'about_x_hours', 1], + [24.hours, false] => [:'x_days', 1], + [30.days, false] => [:'about_x_months', 1], + [60.days, false] => [:'x_months', 2], + [1.year, false] => [:'about_x_years', 1], + [3.years + 6.months, false] => [:'over_x_years', 3], + [3.years + 10.months, false] => [:'almost_x_years', 4] }.each do |passed, expected| assert_distance_of_time_in_words_translates_key passed, expected diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index 51a340d..9fb2080 100644 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -53,13 +53,14 @@ class DateHelperTest < ActionView::TestCase assert_equal "about 2 hours", distance_of_time_in_words(from, to + 89.minutes + 30.seconds) assert_equal "about 24 hours", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 29.seconds) - # 1440..2879 + # 1440..2529 assert_equal "1 day", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 30.seconds) - assert_equal "1 day", distance_of_time_in_words(from, to + 47.hours + 59.minutes + 29.seconds) + assert_equal "1 day", distance_of_time_in_words(from, to + 41.hours + 59.minutes + 29.seconds) - # 2880..43199 - assert_equal "2 days", distance_of_time_in_words(from, to + 47.hours + 59.minutes + 30.seconds) - assert_equal "29 days", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 29.seconds) + # 2530..43199 + assert_equal "2 days", distance_of_time_in_words(from, to + 42.hours + 59.minutes + 30.seconds) + assert_equal "3 days", distance_of_time_in_words(from, to + 2.days + 12.hours) + assert_equal "30 days", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 29.seconds) # 43200..86399 assert_equal "about 1 month", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 30.seconds) @@ -70,13 +71,27 @@ class DateHelperTest < ActionView::TestCase assert_equal "12 months", distance_of_time_in_words(from, to + 1.years - 31.seconds) # > 525599 - assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years - 30.seconds) - assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years + 6.months - 1.day) - assert_equal "over 1 year", distance_of_time_in_words(from, to + 1.years + 6.months) - assert_equal "about 2 years", distance_of_time_in_words(from, to + 2.years + 30.seconds) - assert_equal "about 5 years", distance_of_time_in_words(from, to + 5.years + 5.months) - assert_equal "over 5 years", distance_of_time_in_words(from, to + 5.years + 6.months) - assert_equal "about 10 years", distance_of_time_in_words(from, to + 10.years) + assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years - 30.seconds) + assert_equal "about 1 year", distance_of_time_in_words(from, to + 1.years + 3.months - 1.day) + assert_equal "over 1 year", distance_of_time_in_words(from, to + 1.years + 6.months) + + assert_equal "almost 2 years", distance_of_time_in_words(from, to + 2.years - 3.months + 1.day) + assert_equal "about 2 years", distance_of_time_in_words(from, to + 2.years + 3.months - 1.day) + assert_equal "over 2 years", distance_of_time_in_words(from, to + 2.years + 3.months + 1.day) + assert_equal "over 2 years", distance_of_time_in_words(from, to + 2.years + 9.months - 1.day) + assert_equal "almost 3 years", distance_of_time_in_words(from, to + 2.years + 9.months + 1.day) + + assert_equal "almost 5 years", distance_of_time_in_words(from, to + 5.years - 3.months + 1.day) + assert_equal "about 5 years", distance_of_time_in_words(from, to + 5.years + 3.months - 1.day) + assert_equal "over 5 years", distance_of_time_in_words(from, to + 5.years + 3.months + 1.day) + assert_equal "over 5 years", distance_of_time_in_words(from, to + 5.years + 9.months - 1.day) + assert_equal "almost 6 years", distance_of_time_in_words(from, to + 5.years + 9.months + 1.day) + + assert_equal "almost 10 years", distance_of_time_in_words(from, to + 10.years - 3.months + 1.day) + assert_equal "about 10 years", distance_of_time_in_words(from, to + 10.years + 3.months - 1.day) + assert_equal "over 10 years", distance_of_time_in_words(from, to + 10.years + 3.months + 1.day) + assert_equal "over 10 years", distance_of_time_in_words(from, to + 10.years + 9.months - 1.day) + assert_equal "almost 11 years", distance_of_time_in_words(from, to + 10.years + 9.months + 1.day) # test to < from assert_equal "about 4 hours", distance_of_time_in_words(from + 4.hours, to) -- 1.6.4.2 From f95a7f896e42bed994a843e0ea8073d0373f59fe Mon Sep 17 00:00:00 2001 From: Chad Woolley Date: Tue, 11 Aug 2009 22:53:55 -0700 Subject: [PATCH 330/779] make mysql and postgresql rebuild databases on every CI build, to prevent breakages such as collation and character set changing Signed-off-by: Yehuda Katz --- ci/ci_build.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/ci_build.rb b/ci/ci_build.rb index 0b9bd3d..7d81fa8 100755 --- a/ci/ci_build.rb +++ b/ci/ci_build.rb @@ -28,14 +28,14 @@ cd "#{root_dir}/activerecord" do puts puts "[CruiseControl] Building ActiveRecord with MySQL" puts - build_results[:activerecord_mysql] = system 'rake test_mysql' + build_results[:activerecord_mysql] = system 'rake mysql:rebuild_databases && rake test_mysql' end cd "#{root_dir}/activerecord" do puts puts "[CruiseControl] Building ActiveRecord with PostgreSQL" puts - build_results[:activerecord_postgresql8] = system 'rake test_postgresql' + build_results[:activerecord_postgresql8] = system 'rake postgresql:rebuild_databases && rake test_postgresql' end cd "#{root_dir}/activerecord" do -- 1.6.4.2 From 1901747001676a24e77b95dc61d81d9201111f5a Mon Sep 17 00:00:00 2001 From: Chad Woolley Date: Tue, 29 Sep 2009 02:28:07 -0700 Subject: [PATCH 331/779] reduce max size of fixture IDs to fix sqlite2 tests, because sqlite2 was getting negative and changing values for ID field. See http://www.sqlite.org/datatypes.html Signed-off-by: Yehuda Katz --- activerecord/lib/active_record/fixtures.rb | 2 +- activerecord/test/cases/fixtures_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 09da78c..1674b49 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -434,7 +434,7 @@ end # Any fixture labeled "DEFAULTS" is safely ignored. class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) - MAX_ID = 2 ** 31 - 1 + MAX_ID = 2 ** 30 - 1 DEFAULT_FILTER_RE = /\.ya?ml$/ @@all_cached_fixtures = {} diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index eb3f03c..f965652 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -519,8 +519,8 @@ class FoxyFixturesTest < ActiveRecord::TestCase end def test_identifies_consistently - assert_equal 1281023246, Fixtures.identify(:ruby) - assert_equal 2140105598, Fixtures.identify(:sapphire_2) + assert_equal 207281424, Fixtures.identify(:ruby) + assert_equal 1066363776, Fixtures.identify(:sapphire_2) end TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on) -- 1.6.4.2 From 1d7412b135cac39a6693cc1efa328e6c7707698d Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Tue, 29 Sep 2009 20:06:46 -0400 Subject: [PATCH 332/779] Reduce specificity of logging test to avoid dependency on the system clock Signed-off-by: Yehuda Katz --- actionmailer/test/mail_service_test.rb | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 3244aad..db6d3df 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -570,7 +570,9 @@ class ActionMailerTest < Test::Unit::TestCase mail = TestMailer.create_signed_up(@recipient) logger = mock() logger.expects(:info).with("Sent mail to #{@recipient}") - logger.expects(:debug).with("\n#{mail.encoded}") + logger.expects(:debug).with() do |logged_text| + logged_text =~ /\[Signed up\] Welcome/ + end TestMailer.logger = logger TestMailer.deliver_signed_up(@recipient) end -- 1.6.4.2 From 11cce5bde9872da95b83ce627f95c2444d1442df Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 5 Oct 2009 14:21:57 -0500 Subject: [PATCH 333/779] Coerce all out going body parts to Strings --- actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/middlewares.rb | 2 + .../lib/action_controller/string_coercion.rb | 29 ++++++++++++++++++++ 3 files changed, 32 insertions(+), 0 deletions(-) create mode 100644 actionpack/lib/action_controller/string_coercion.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index f53ba27..e94d492 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -70,6 +70,7 @@ module ActionController autoload :SessionManagement, 'action_controller/session_management' autoload :StatusCodes, 'action_controller/status_codes' autoload :Streaming, 'action_controller/streaming' + autoload :StringCoercion, 'action_controller/string_coercion' autoload :TestCase, 'action_controller/test_case' autoload :TestProcess, 'action_controller/test_process' autoload :Translation, 'action_controller/translation' diff --git a/actionpack/lib/action_controller/middlewares.rb b/actionpack/lib/action_controller/middlewares.rb index dda25fc..16eb363 100644 --- a/actionpack/lib/action_controller/middlewares.rb +++ b/actionpack/lib/action_controller/middlewares.rb @@ -10,3 +10,5 @@ use lambda { ActionController::Base.session_store }, use "ActionController::ParamsParser" use "Rack::MethodOverride" use "Rack::Head" + +use "ActionController::StringCoercion" diff --git a/actionpack/lib/action_controller/string_coercion.rb b/actionpack/lib/action_controller/string_coercion.rb new file mode 100644 index 0000000..6d8c24e --- /dev/null +++ b/actionpack/lib/action_controller/string_coercion.rb @@ -0,0 +1,29 @@ +module ActionController + class StringCoercion + class UglyBody < ActiveSupport::BasicObject + def initialize(body) + @body = body + end + + def each + @body.each do |part| + yield part.to_s + end + end + + private + def method_missing(*args, &block) + @body.__send__(*args, &block) + end + end + + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + [status, headers, UglyBody.new(body)] + end + end +end -- 1.6.4.2 From f98302e46b66a8e4ce7038944901c8066130b083 Mon Sep 17 00:00:00 2001 From: Jeffrey Hardy Date: Mon, 5 Oct 2009 09:06:57 -0400 Subject: [PATCH 334/779] MessageVerifier#verify raises InvalidSignature if the signature is blank Signed-off-by: Jeremy Kemper --- .../lib/active_support/message_verifier.rb | 2 ++ activesupport/test/message_verifier_test.rb | 5 +++++ 2 files changed, 7 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index e76a03f..acc600d 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -24,6 +24,8 @@ module ActiveSupport end def verify(signed_message) + raise InvalidSignature if signed_message.blank? + data, digest = signed_message.split("--") if secure_compare(digest, generate_digest(data)) Marshal.load(ActiveSupport::Base64.decode64(data)) diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb index 57c4ce8..d8c6600 100644 --- a/activesupport/test/message_verifier_test.rb +++ b/activesupport/test/message_verifier_test.rb @@ -11,6 +11,11 @@ class MessageVerifierTest < Test::Unit::TestCase assert_equal @data, @verifier.verify(message) end + def test_missing_signature_raises + assert_not_verified(nil) + assert_not_verified("") + end + def test_tampered_data_raises data, hash = @verifier.generate(@data).split("--") assert_not_verified("#{data.reverse}--#{hash}") -- 1.6.4.2 From 91b61a8d1622d5e03c59d801a96fc26f4cb02ed8 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 6 Oct 2009 15:55:56 +0100 Subject: [PATCH 335/779] Monkey patch Rack::Lint to allow string subclass body --- actionpack/lib/action_controller/integration.rb | 1 + .../lib/action_controller/rack_lint_patch.rb | 36 ++++++++++++++++++++ actionpack/test/controller/integration_test.rb | 20 +++++++++++ 3 files changed, 57 insertions(+), 0 deletions(-) create mode 100644 actionpack/lib/action_controller/rack_lint_patch.rb diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 58b6476..80ad089 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -1,6 +1,7 @@ require 'stringio' require 'uri' require 'active_support/test_case' +require 'action_controller/rack_lint_patch' module ActionController module Integration #:nodoc: diff --git a/actionpack/lib/action_controller/rack_lint_patch.rb b/actionpack/lib/action_controller/rack_lint_patch.rb new file mode 100644 index 0000000..6938e8e --- /dev/null +++ b/actionpack/lib/action_controller/rack_lint_patch.rb @@ -0,0 +1,36 @@ +# Rack 1.0 does not allow string subclass body. This does not play well with our ActionView::SafeBuffer. +# The next release of Rack will be allowing string subclass body - http://github.com/rack/rack/commit/de668df02802a0335376a81ba709270e43ba9d55 +# TODO : Remove this monkey patch after the next release of Rack + +module RackLintPatch + module AllowStringSubclass + def self.included(base) + base.send :alias_method, :each, :each_with_hack + end + + def each_with_hack + @closed = false + + @body.each { |part| + assert("Body yielded non-string value #{part.inspect}") { + part.kind_of?(String) + } + yield part + } + + if @body.respond_to?(:to_path) + assert("The file identified by body.to_path does not exist") { + ::File.exist? @body.to_path + } + end + end + end + + begin + app = proc {|env| [200, {"Content-Type" => "text/plain", "Content-Length" => "12"}, [Class.new(String).new("Hello World!")]] } + response = Rack::MockRequest.new(Rack::Lint.new(app)).get('/') + rescue Rack::Lint::LintError => e + raise(e) unless e.message =~ /Body yielded non-string value/ + Rack::Lint.send :include, AllowStringSubclass + end +end diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index b8281b5..fe8e3d3 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -443,3 +443,23 @@ class MetalTest < ActionController::IntegrationTest assert_equal '', response.body end end + +class StringSubclassBodyTest < ActionController::IntegrationTest + class SafeString < String + end + + class SafeStringMiddleware + def self.call(env) + [200, {"Content-Type" => "text/plain", "Content-Length" => "12"}, [SafeString.new("Hello World!")]] + end + end + + def setup + @integration_session = ActionController::Integration::Session.new(SafeStringMiddleware) + end + + def test_string_subclass_body + get '/' + assert_equal 'Hello World!', response.body + end +end -- 1.6.4.2 From 58f14438a957997dd8aca4101f6204b08cf19747 Mon Sep 17 00:00:00 2001 From: Jesse Proudman Date: Sat, 26 Sep 2009 14:53:27 -0700 Subject: [PATCH 336/779] Running rake dev leaves ERB in environment.rb. The existing Rake task was just copying the file across. This patch modifies the Rake task to use the same ERB processing string used on database.yml, which cleans up the environment.rb file. Signed-off-by: Michael Koziarski --- railties/Rakefile | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/Rakefile b/railties/Rakefile index b7c199f..4911d3f 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -202,7 +202,7 @@ task :copy_configs do cp "configs/seeds.rb", "#{PKG_DESTINATION}/db/seeds.rb" cp "environments/boot.rb", "#{PKG_DESTINATION}/config/boot.rb" - cp "environments/environment.rb", "#{PKG_DESTINATION}/config/environment.rb" + File.open("#{PKG_DESTINATION}/config/environment.rb", 'w') {|f| f.write ERB.new(IO.read("environments/environment.rb"), nil, '-').result(binding)} cp "environments/production.rb", "#{PKG_DESTINATION}/config/environments/production.rb" cp "environments/development.rb", "#{PKG_DESTINATION}/config/environments/development.rb" cp "environments/test.rb", "#{PKG_DESTINATION}/config/environments/test.rb" -- 1.6.4.2 From 0f14d7b6d37940a93c12c373e6e92e88f2dc96d7 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 24 Aug 2009 17:16:13 -0400 Subject: [PATCH 337/779] Only load rake tasks from tasks/**/*.rake and lib/tasks/**/*.rake in plugins Previously, it was **/tasks/**/*.rake, and that loaded some paths that shouldn't be like: * vendor/plugins/admin_assistant/test_rails_app/lib/tasks/rspec.rake * vendor/plugins/will_paginate/test/tasks.rake Signed-off-by: Michael Koziarski --- railties/lib/tasks/rails.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/railties/lib/tasks/rails.rb b/railties/lib/tasks/rails.rb index 8c2b7f9..0600a05 100644 --- a/railties/lib/tasks/rails.rb +++ b/railties/lib/tasks/rails.rb @@ -4,5 +4,6 @@ $VERBOSE = nil Dir["#{File.dirname(__FILE__)}/*.rake"].each { |ext| load ext } # Load any custom rakefile extensions -Dir["#{RAILS_ROOT}/vendor/plugins/*/**/tasks/**/*.rake"].sort.each { |ext| load ext } +Dir["#{RAILS_ROOT}/vendor/plugins/*/tasks/**/*.rake"].sort.each { |ext| load ext } +Dir["#{RAILS_ROOT}/vendor/plugins/*/lib/tasks/**/*.rake"].sort.each { |ext| load ext } Dir["#{RAILS_ROOT}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } -- 1.6.4.2 From 6f2c4991efbbc5f567a6df36ca78de4f3ca24ee2 Mon Sep 17 00:00:00 2001 From: pivotal Date: Wed, 5 Aug 2009 18:36:34 -0700 Subject: [PATCH 338/779] Explicitly require ActionController's CGI extensions so they're properly loaded before the first request. Signed-off-by: Michael Koziarski --- actionpack/lib/action_controller.rb | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index e94d492..a59dd76 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -33,6 +33,7 @@ end gem 'rack', '~> 1.0.0' require 'rack' +require 'action_controller/cgi_ext' module ActionController # TODO: Review explicit to see if they will automatically be handled by -- 1.6.4.2 From e2127991a188393b0188cffd57227e33f0a513ae Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 7 Oct 2009 23:45:40 +0100 Subject: [PATCH 339/779] Allow accepts_nested_attributes_for :reject_if option accept symbols for using a method --- .../lib/active_record/nested_attributes.rb | 37 +++++++++++++++++--- activerecord/test/cases/nested_attributes_test.rb | 20 +++++++++++ activerecord/test/models/pirate.rb | 4 ++ 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index b583e62..97feed9 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -123,6 +123,22 @@ module ActiveRecord # member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!' # member.posts.second.title # => 'The egalitarian assumption of the modern citizen' # + # Alternatively, :reject_if also accepts a symbol for using methods: + # + # class Member < ActiveRecord::Base + # has_many :posts + # accepts_nested_attributes_for :posts, :reject_if => :new_record? + # end + # + # class Member < ActiveRecord::Base + # has_many :posts + # accepts_nested_attributes_for :posts, :reject_if => :reject_posts + # + # def reject_posts(attributed) + # attributed['title].blank? + # end + # end + # # If the hash contains an id key that matches an already # associated record, the matching record will be modified: # @@ -175,9 +191,10 @@ module ActiveRecord # _destroy key and a value that evaluates to +true+ # (eg. 1, '1', true, or 'true'). This option is off by default. # [:reject_if] - # Allows you to specify a Proc that checks whether a record should be - # built for a certain attribute hash. The hash is passed to the Proc - # and the Proc should return either +true+ or +false+. When no Proc + # Allows you to specify a Proc or a Symbol pointing to a method + # that checks whether a record should be built for a certain attribute + # hash. The hash is passed to the supplied Proc or the method + # and it should return either +true+ or +false+. When no :reject_if # is specified, a record will be built for all attribute hashes that # do not have a _destroy value that evaluates to true. # @@ -338,8 +355,18 @@ module ActiveRecord # has_destroy_flag? or if a :reject_if proc exists for this # association and evaluates to +true+. def reject_new_record?(association_name, attributes) - has_destroy_flag?(attributes) || - self.class.reject_new_nested_attributes_procs[association_name].try(:call, attributes) + has_destroy_flag?(attributes) || call_reject_if(association_name, attributes) + end + + def call_reject_if(association_name, attributes) + callback = self.class.reject_new_nested_attributes_procs[association_name] + + case callback + when Symbol + method(callback).arity == 0 ? send(callback) : send(callback, attributes) + when Proc + callback.try(:call, attributes) + end end end end diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 792d6d1..a0cccc3 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -65,6 +65,26 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase ship = Ship.create!(:name => 'Nights Dirty Lightning') ship._delete end + + def test_reject_if_method_without_arguments + Pirate.accepts_nested_attributes_for :ship, :reject_if => :new_record? + + pirate = Pirate.new(:catchphrase => "Stop wastin' me time") + pirate.ship_attributes = { :name => 'Black Pearl' } + assert_no_difference('Ship.count') { pirate.save! } + end + + def test_reject_if_method_with_arguments + Pirate.accepts_nested_attributes_for :ship, :reject_if => :reject_empty_ships_on_create + + pirate = Pirate.new(:catchphrase => "Stop wastin' me time") + pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true } + assert_no_difference('Ship.count') { pirate.save! } + + # pirate.reject_empty_ships_on_create returns false for saved records + pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true } + assert_difference('Ship.count') { pirate.save! } + end end class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index bcf23f0..9616d05 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -43,6 +43,10 @@ class Pirate < ActiveRecord::Base @ship_log ||= [] end + def reject_empty_ships_on_create(attributes) + attributes.delete('_reject_me_if_new').present? && new_record? + end + private def log_before_add(record) log(record, "before_adding_method") -- 1.6.4.2 From a69316b293e26031de4fcabc481331180608098a Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 8 Oct 2009 00:14:37 +0100 Subject: [PATCH 340/779] Use indifferent access attributes instead of stringifying them --- .../lib/active_record/nested_attributes.rb | 4 ++-- activerecord/test/cases/nested_attributes_test.rb | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 97feed9..d0df903 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -270,7 +270,7 @@ module ActiveRecord # :_destroy key set to a truthy value, then the existing record # will be marked for destruction. def assign_nested_attributes_for_one_to_one_association(association_name, attributes, allow_destroy) - attributes = attributes.stringify_keys + attributes = attributes.with_indifferent_access if attributes['id'].blank? unless reject_new_record?(association_name, attributes) @@ -323,7 +323,7 @@ module ActiveRecord end attributes_collection.each do |attributes| - attributes = attributes.stringify_keys + attributes = attributes.with_indifferent_access if attributes['id'].blank? unless reject_new_record?(association_name, attributes) diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index a0cccc3..5ef3ec4 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -85,6 +85,14 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true } assert_difference('Ship.count') { pirate.save! } end + + def test_reject_if_with_indifferent_keys + Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| attributes[:name].blank? } + + pirate = Pirate.new(:catchphrase => "Stop wastin' me time") + pirate.ship_attributes = { :name => 'Hello Pearl' } + assert_difference('Ship.count') { pirate.save! } + end end class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase -- 1.6.4.2 From 80da8eb43dfabb4ca9f0adcb431882d03e6388bb Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Tue, 6 Oct 2009 16:03:03 +1300 Subject: [PATCH 341/779] Merge the prerequisites for on-by-default XSS escaping into rails. This consists of: * String#html_safe! a method to mark a string as 'safe' * ActionView::SafeBuffer a string subclass which escapes anything unsafe which is concatenated to it * Calls to String#html_safe! throughout the rails helpers * a 'raw' helper which lets you concatenate trusted HTML from non-safety-aware sources (e.g. presantized strings in the DB) Note, this does *not* give you on-by-default XSS escaping in 2.3 applications. To get that you'll need to install a plugin: http://github.com/nzkoz/rails_xss --- actionpack/lib/action_view.rb | 6 +- actionpack/lib/action_view/erb/util.rb | 5 + actionpack/lib/action_view/helpers.rb | 2 + .../action_view/helpers/active_record_helper.rb | 2 +- .../lib/action_view/helpers/asset_tag_helper.rb | 4 +- .../lib/action_view/helpers/capture_helper.rb | 4 +- actionpack/lib/action_view/helpers/date_helper.rb | 6 +- actionpack/lib/action_view/helpers/form_helper.rb | 4 +- .../lib/action_view/helpers/form_options_helper.rb | 2 +- .../lib/action_view/helpers/form_tag_helper.rb | 6 +- .../lib/action_view/helpers/prototype_helper.rb | 2 +- .../lib/action_view/helpers/raw_output_helper.rb | 9 ++ .../lib/action_view/helpers/sanitize_helper.rb | 12 +++- actionpack/lib/action_view/helpers/tag_helper.rb | 8 +- actionpack/lib/action_view/helpers/url_helper.rb | 6 +- actionpack/lib/action_view/partials.rb | 2 +- actionpack/lib/action_view/safe_buffer.rb | 28 +++++++ actionpack/test/template/asset_tag_helper_test.rb | 12 +++ actionpack/test/template/form_helper_test.rb | 2 +- actionpack/test/template/raw_output_helper_test.rb | 21 +++++ actionpack/test/template/sanitize_helper_test.rb | 11 +++- actionpack/test/template/tag_helper_test.rb | 1 + actionpack/test/view/safe_buffer_test.rb | 36 ++++++++ .../lib/active_support/core_ext/string.rb | 2 + .../core_ext/string/output_safety.rb | 48 +++++++++++ activesupport/test/core_ext/string_ext_test.rb | 86 ++++++++++++++++++++ 26 files changed, 297 insertions(+), 30 deletions(-) create mode 100644 actionpack/lib/action_view/helpers/raw_output_helper.rb create mode 100644 actionpack/lib/action_view/safe_buffer.rb create mode 100644 actionpack/test/template/raw_output_helper_test.rb create mode 100644 actionpack/test/view/safe_buffer_test.rb create mode 100644 activesupport/lib/active_support/core_ext/string/output_safety.rb diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 1f1ff9d..025745c 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -49,10 +49,10 @@ module ActionView autoload :TemplateHandler, 'action_view/template_handler' autoload :TemplateHandlers, 'action_view/template_handlers' autoload :Helpers, 'action_view/helpers' + autoload :SafeBuffer, 'action_view/safe_buffer' end -class ERB - autoload :Util, 'action_view/erb/util' -end +require 'action_view/erb/util' + I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml" diff --git a/actionpack/lib/action_view/erb/util.rb b/actionpack/lib/action_view/erb/util.rb index 3c77c5c..3bf877f 100644 --- a/actionpack/lib/action_view/erb/util.rb +++ b/actionpack/lib/action_view/erb/util.rb @@ -18,6 +18,11 @@ class ERB s.to_s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] } end + alias h html_escape + + module_function :html_escape + module_function :h + # A utility method for escaping HTML entities in JSON strings. # This method is also aliased as j. # diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb index 97fa2d8..cea894d 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -14,6 +14,7 @@ module ActionView #:nodoc: autoload :JavaScriptHelper, 'action_view/helpers/javascript_helper' autoload :NumberHelper, 'action_view/helpers/number_helper' autoload :PrototypeHelper, 'action_view/helpers/prototype_helper' + autoload :RawOutputHelper, 'action_view/helpers/raw_output_helper' autoload :RecordIdentificationHelper, 'action_view/helpers/record_identification_helper' autoload :RecordTagHelper, 'action_view/helpers/record_tag_helper' autoload :SanitizeHelper, 'action_view/helpers/sanitize_helper' @@ -45,6 +46,7 @@ module ActionView #:nodoc: include JavaScriptHelper include NumberHelper include PrototypeHelper + include RawOutputHelper include RecordIdentificationHelper include RecordTagHelper include SanitizeHelper diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 541899e..355f098 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -290,7 +290,7 @@ module ActionView end def error_wrapping(html_tag, has_error) - has_error ? Base.field_error_proc.call(html_tag, self) : html_tag + has_error ? Base.field_error_proc.call(html_tag, self).html_safe! : html_tag end def error_message diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index babb9db..574b384 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -285,7 +285,7 @@ module ActionView end javascript_src_tag(joined_javascript_name, options) else - expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n") + expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n").html_safe! end end @@ -434,7 +434,7 @@ module ActionView end stylesheet_tag(joined_stylesheet_name, options) else - expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n") + expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n").html_safe! end end diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index e86ca27..40411c2 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -118,13 +118,13 @@ module ActionView def content_for(name, content = nil, &block) ivar = "@content_for_#{name}" content = capture(&block) if block_given? - instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}") + instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}".html_safe!) nil end # Use an alternate output buffer for the duration of the block. # Defaults to a new empty string. - def with_output_buffer(buf = '') #:nodoc: + def with_output_buffer(buf = "") #:nodoc: self.output_buffer, old_buffer = buf, output_buffer yield output_buffer diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 4ba48e7..e7b2bf2 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -916,15 +916,15 @@ module ActionView class InstanceTag #:nodoc: def to_date_select_tag(options = {}, html_options = {}) - datetime_selector(options, html_options).select_date + datetime_selector(options, html_options).select_date.html_safe! end def to_time_select_tag(options = {}, html_options = {}) - datetime_selector(options, html_options).select_time + datetime_selector(options, html_options).select_time.html_safe! end def to_datetime_select_tag(options = {}, html_options = {}) - datetime_selector(options, html_options).select_datetime + datetime_selector(options, html_options).select_datetime.html_safe! end private diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 0e097e6..994fac0 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -280,7 +280,7 @@ module ActionView concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {})) fields_for(object_name, *(args << options), &proc) - concat('') + concat(''.html_safe!) end def apply_form_for_options!(object_or_array, options) #:nodoc: @@ -797,7 +797,7 @@ module ActionView add_default_name_and_id(options) hidden = tag("input", "name" => options["name"], "type" => "hidden", "value" => options['disabled'] && checked ? checked_value : unchecked_value) checkbox = tag("input", options) - hidden + checkbox + (hidden + checkbox).html_safe! end def to_boolean_select_tag(options = {}) diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index ec0e3d6..608a3f1 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -296,7 +296,7 @@ module ActionView options << %() end - options_for_select.join("\n") + options_for_select.join("\n").html_safe! end # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index c217191..eaba603 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -432,7 +432,7 @@ module ActionView concat(tag(:fieldset, options, true)) concat(content_tag(:legend, legend)) unless legend.blank? concat(content) - concat("") + concat("".html_safe!) end private @@ -459,14 +459,14 @@ module ActionView def form_tag_html(html_options) extra_tags = extra_tags_for_form(html_options) - tag(:form, html_options, true) + extra_tags + (tag(:form, html_options, true) + extra_tags).html_safe! end def form_tag_in_block(html_options, &block) content = capture(&block) concat(form_tag_html(html_options)) concat(content) - concat("") + concat("".html_safe!) end def token_tag diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 448d6f5..75f5c86 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -393,7 +393,7 @@ module ActionView concat(form_remote_tag(options)) fields_for(object_name, *(args << options), &proc) - concat('') + concat(''.html_safe!) end alias_method :form_remote_for, :remote_form_for diff --git a/actionpack/lib/action_view/helpers/raw_output_helper.rb b/actionpack/lib/action_view/helpers/raw_output_helper.rb new file mode 100644 index 0000000..79b0e4e --- /dev/null +++ b/actionpack/lib/action_view/helpers/raw_output_helper.rb @@ -0,0 +1,9 @@ +module ActionView #:nodoc: + module Helpers #:nodoc: + module RawOutputHelper + def raw(stringish) + stringish.to_s.html_safe! + end + end + end +end \ No newline at end of file diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index d89b955..69d0d0f 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -49,7 +49,11 @@ module ActionView # confuse browsers. # def sanitize(html, options = {}) - self.class.white_list_sanitizer.sanitize(html, options) + returning self.class.white_list_sanitizer.sanitize(html, options) do |sanitized| + if sanitized + sanitized.html_safe! + end + end end # Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute. @@ -72,7 +76,11 @@ module ActionView # strip_tags("
Welcome to my website!
") # # => Welcome to my website! def strip_tags(html) - self.class.full_sanitizer.sanitize(html) + returning self.class.full_sanitizer.sanitize(html) do |sanitized| + if sanitized + sanitized.html_safe! + end + end end # Strips all link tags from +text+ leaving just the link text. diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index db99a0e..c47e74b 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -38,7 +38,7 @@ module ActionView # tag("img", { :src => "open & shut.png" }, false, false) # # => def tag(name, options = nil, open = false, escape = true) - "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}" + "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe! end # Returns an HTML block tag of type +name+ surrounding the +content+. Add @@ -91,7 +91,7 @@ module ActionView # cdata_section(File.read("hello_world.txt")) # # => def cdata_section(content) - "" + "".html_safe! end # Returns an escaped version of +html+ without affecting existing escaped entities. @@ -125,7 +125,7 @@ module ActionView def content_tag_string(name, content, options, escape = true) tag_options = tag_options(options, escape) if options - "<#{name}#{tag_options}>#{content}" + "<#{name}#{tag_options}>#{content}".html_safe! end def tag_options(options, escape = true) @@ -142,7 +142,7 @@ module ActionView else attrs = options.map { |key, value| %(#{key}="#{value}") } end - " #{attrs.sort * ' '}" unless attrs.empty? + " #{attrs.sort * ' '}".html_safe! unless attrs.empty? end end end diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index f692af1..d106e68 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -219,7 +219,7 @@ module ActionView if block_given? options = args.first || {} html_options = args.second - concat(link_to(capture(&block), options, html_options)) + concat(link_to(capture(&block), options, html_options).html_safe!) else name = args.first options = args.second || {} @@ -237,7 +237,7 @@ module ActionView end href_attr = "href=\"#{url}\"" unless href - "#{name || url}" + "#{name || url}".html_safe! end end @@ -309,7 +309,7 @@ module ActionView html_options.merge!("type" => "submit", "value" => name) "
" + - method_tag + tag("input", html_options) + request_token_tag + "
" + method_tag + tag("input", html_options) + request_token_tag + "".html_safe! end diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index 9e5e0f7..5d75dbb 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -221,7 +221,7 @@ module ActionView result = template.render_partial(self, object, local_assigns.dup, as) index += 1 result - end.join(spacer) + end.join(spacer).html_safe! end def _pick_partial_template(partial_path) #:nodoc: diff --git a/actionpack/lib/action_view/safe_buffer.rb b/actionpack/lib/action_view/safe_buffer.rb new file mode 100644 index 0000000..09f44ab --- /dev/null +++ b/actionpack/lib/action_view/safe_buffer.rb @@ -0,0 +1,28 @@ + +module ActionView #:nodoc: + class SafeBuffer < String + def <<(value) + if value.html_safe? + super(value) + else + super(ERB::Util.h(value)) + end + end + + def concat(value) + self << value + end + + def html_safe? + true + end + + def html_safe! + self + end + + def to_s + self + end + end +end \ No newline at end of file diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 7ffabff..46e6129 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -164,6 +164,11 @@ class AssetTagHelperTest < ActionView::TestCase assert_dom_equal(%(\n\n\n\n), javascript_include_tag(:defaults)) end + def test_javascript_include_tag_is_html_safe + assert javascript_include_tag(:defaults).html_safe? + assert javascript_include_tag("prototype").html_safe? + end + def test_register_javascript_include_default ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'slider' @@ -206,6 +211,13 @@ class AssetTagHelperTest < ActionView::TestCase StyleLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end + def test_stylesheet_link_tag_is_html_safe + ENV["RAILS_ASSET_ID"] = "" + assert stylesheet_link_tag('dir/file').html_safe? + assert stylesheet_link_tag('dir/other/file', 'dir/file2').html_safe? + assert stylesheet_tag('dir/file', {}).html_safe? + end + def test_custom_stylesheet_expansions ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :monkey => ["head", "body", "tail"] assert_dom_equal %(\n\n\n\n), stylesheet_link_tag('first', :monkey, 'last') diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 41e1583..357c36d 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1060,7 +1060,7 @@ class FormHelperTest < ActionView::TestCase (field_helpers - %w(hidden_field)).each do |selector| src = <<-END_SRC def #{selector}(field, *args, &proc) - " " + super + "
" + (" " + super + "
").html_safe! end END_SRC class_eval src, __FILE__, __LINE__ diff --git a/actionpack/test/template/raw_output_helper_test.rb b/actionpack/test/template/raw_output_helper_test.rb new file mode 100644 index 0000000..598aa5b --- /dev/null +++ b/actionpack/test/template/raw_output_helper_test.rb @@ -0,0 +1,21 @@ +require 'abstract_unit' +require 'testing_sandbox' + +class RawOutputHelperTest < ActionView::TestCase + tests ActionView::Helpers::RawOutputHelper + include TestingSandbox + + def setup + @string = "hello" + end + + test "raw returns the safe string" do + result = raw(@string) + assert_equal @string, result + assert result.html_safe? + end + + test "raw handles nil values correctly" do + assert_equal "", raw(nil) + end +end \ No newline at end of file diff --git a/actionpack/test/template/sanitize_helper_test.rb b/actionpack/test/template/sanitize_helper_test.rb index f715071..222d4db 100644 --- a/actionpack/test/template/sanitize_helper_test.rb +++ b/actionpack/test/template/sanitize_helper_test.rb @@ -39,7 +39,16 @@ class SanitizeHelperTest < ActionView::TestCase %{This is a test.\n\n\nIt no longer contains any HTML.\n}, strip_tags( %{This is <b>a <a href="" target="_blank">test</a></b>.\n\n\n\n

It no longer contains any HTML.

\n})) assert_equal "This has a here.", strip_tags("This has a here.") - [nil, '', ' '].each { |blank| assert_equal blank, strip_tags(blank) } + [nil, '', ' '].each do |blank| + stripped = strip_tags(blank) + assert_equal blank, stripped + assert stripped.html_safe? unless blank.nil? + end + assert strip_tags("").html_safe? end def assert_sanitized(text, expected = nil) diff --git a/actionpack/test/template/tag_helper_test.rb b/actionpack/test/template/tag_helper_test.rb index ef88cae..2acf6af 100644 --- a/actionpack/test/template/tag_helper_test.rb +++ b/actionpack/test/template/tag_helper_test.rb @@ -34,6 +34,7 @@ class TagHelperTest < ActionView::TestCase def test_content_tag assert_equal "Create", content_tag("a", "Create", "href" => "create") + assert content_tag("a", "Create", "href" => "create").html_safe? assert_equal content_tag("a", "Create", "href" => "create"), content_tag("a", "Create", :href => "create") end diff --git a/actionpack/test/view/safe_buffer_test.rb b/actionpack/test/view/safe_buffer_test.rb new file mode 100644 index 0000000..0b378ae --- /dev/null +++ b/actionpack/test/view/safe_buffer_test.rb @@ -0,0 +1,36 @@ +require 'abstract_unit' + +class SafeBufferTest < ActionView::TestCase + def setup + @buffer = ActionView::SafeBuffer.new + end + + test "Should look like a string" do + assert @buffer.is_a?(String) + assert_equal "", @buffer + end + + test "Should escape a raw string which is passed to them" do + @buffer << "" @@ -490,9 +488,9 @@ module ActionView char = c.chr string << (char =~ /\w/ ? sprintf("%%%x", c) : char) end - content_tag "a", name || email_address_encoded, html_options.merge({ "href" => "#{string}#{extras}" }) + content_tag "a", name || email_address_encoded.html_safe, html_options.merge({ "href" => "#{string}#{extras}" }) else - content_tag "a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" }) + content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" }) end end -- 1.6.4.2 From 397262a4ee92484e307c93d7450025a677891f08 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 19 Feb 2010 19:57:08 -0200 Subject: [PATCH 503/779] i18n translate with arrays issue solved --- .../lib/action_view/helpers/translation_helper.rb | 3 ++- .../test/template/translation_helper_test.rb | 5 +++++ 2 files changed, 7 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index dc5ad5e..bcdeda1 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -12,7 +12,8 @@ module ActionView # prepend the key with a period, nothing is converted. def translate(key, options = {}) options[:raise] = true - I18n.translate(scope_key_by_partial(key), options).html_safe + translation = I18n.translate(scope_key_by_partial(key), options) + translation.is_a?(Array) ? translation.map { |entry| entry.html_safe } : translation.html_safe rescue I18n::MissingTranslationData => e keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope]) content_tag('span', keys.join(', '), :class => 'translation_missing') diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index d67d2c7..0a9efc7 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -17,6 +17,11 @@ class TranslationHelperTest < Test::Unit::TestCase expected = 'en, foo' assert_equal expected, translate(:foo) end + + def test_translation_of_an_array + I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"]) + assert_equal ["foo", "bar"], translate(["foo", "bar"]) + end def test_delegates_localize_to_i18n @time = Time.utc(2008, 7, 8, 12, 18, 38) -- 1.6.4.2 From 64d28f61ada584faf4d569ae9dd2a42891a27a46 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 19 Feb 2010 20:00:07 -0200 Subject: [PATCH 504/779] ruby 1.9 array.to_s returns a string representing an escaped array --- .../lib/action_view/helpers/translation_helper.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index bcdeda1..afa592d 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -29,7 +29,7 @@ module ActionView private def scope_key_by_partial(key) - if key.to_s.first == "." + if (key.respond_to?(:join) ? key.join : key.to_s).first == "." template.path_without_format_and_extension.gsub(%r{/_?}, ".") + key.to_s else key -- 1.6.4.2 From 24911757de417588d0ceee4b71018ef0641ce042 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 19 Feb 2010 20:23:43 -0200 Subject: [PATCH 505/779] missings html_safe added --- .../action_view/helpers/active_record_helper.rb | 6 +++--- actionpack/lib/action_view/helpers/date_helper.rb | 6 +++--- .../lib/action_view/helpers/form_tag_helper.rb | 2 +- .../lib/action_view/helpers/javascript_helper.rb | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 08c7d32..ed407c9 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -121,7 +121,7 @@ module ActionView if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) && (errors = obj.errors.on(method)) content_tag("div", - "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.is_a?(Array) ? errors.first : errors)}#{options[:append_text]}", + "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.is_a?(Array) ? errors.first : errors)}#{options[:append_text]}".html_safe, :class => options[:css_class] ) else @@ -198,14 +198,14 @@ module ActionView locale.t :header, :count => count, :model => object_name end message = options.include?(:message) ? options[:message] : locale.t(:body) - error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, ERB::Util.html_escape(msg)) } }.join + error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, ERB::Util.html_escape(msg)) } }.join.html_safe contents = '' contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank? contents << content_tag(:p, message) unless message.blank? contents << content_tag(:ul, error_messages) - content_tag(:div, contents, html) + content_tag(:div, contents.html_safe, html) end else '' diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 054f376..add242d 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -815,7 +815,7 @@ module ActionView tag_options[:selected] = "selected" if selected == i select_options << content_tag(:option, value, tag_options) end - select_options.join("\n") + "\n" + (select_options.join("\n") + "\n").html_safe end # Builds select tag from date type and html select options @@ -833,9 +833,9 @@ module ActionView select_html = "\n" select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank] select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt] - select_html << select_options_as_html.to_s + select_html << select_options_as_html - (content_tag(:select, select_html, select_options) + "\n").html_safe + (content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe end # Builds a prompt option tag with supplied options or from default options diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 13f8c77..c0382da 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -262,7 +262,7 @@ module ActionView escape = options.key?("escape") ? options.delete("escape") : true content = html_escape(content) if escape - content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) + content_tag :textarea, content.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end # Creates a check box form input tag. diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 8f64acf..2ed9d23 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -184,7 +184,7 @@ module ActionView end def javascript_cdata_section(content) #:nodoc: - "\n//#{cdata_section("\n#{content}\n//")}\n" + "\n//#{cdata_section("\n#{content}\n//")}\n".html_safe end protected -- 1.6.4.2 From 39bcf14b3466ef2e3ae2b3af876e50981de93c5f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 19 Feb 2010 21:23:13 -0200 Subject: [PATCH 506/779] missing html_safe added and tests --- .../lib/action_view/helpers/form_options_helper.rb | 5 ++--- actionpack/test/template/form_tag_helper_test.rb | 12 ++++++------ actionpack/test/template/url_helper_test.rb | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index ecb822d..6d5acf1 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -572,10 +572,9 @@ module ActionView end if value.blank? && options[:prompt] prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('support.select.prompt', :default => 'Please select') - "\n" + option_tags - else - option_tags + option_tags = "\n" + option_tags end + option_tags.html_safe end end diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 30a3c70..2559b7a 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -113,19 +113,19 @@ class FormTagHelperTest < ActionView::TestCase end def test_select_tag - actual = select_tag "people", "" + actual = select_tag "people", "".html_safe expected = %() assert_dom_equal expected, actual end def test_select_tag_with_multiple - actual = select_tag "colors", "", :multiple => :true + actual = select_tag "colors", "".html_safe, :multiple => :true expected = %() assert_dom_equal expected, actual end def test_select_tag_disabled - actual = select_tag "places", "", :disabled => :true + actual = select_tag "places", "".html_safe, :disabled => :true expected = %() assert_dom_equal expected, actual end @@ -256,9 +256,9 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal %(), check_box_tag("admin", 1, true, 'disabled' => true, :readonly => "yes") assert_dom_equal %(), check_box_tag("admin", 1, true, :disabled => false, :readonly => nil) assert_dom_equal %(), tag(:input, :type => "checkbox", :checked => false) - assert_dom_equal %(), select_tag("people", "", :multiple => true) - assert_dom_equal %(), select_tag("people[]", "", :multiple => true) - assert_dom_equal %(), select_tag("people", "", :multiple => nil) + assert_dom_equal %(), select_tag("people", "".html_safe, :multiple => true) + assert_dom_equal %(), select_tag("people[]", "".html_safe, :multiple => true) + assert_dom_equal %(), select_tag("people", "".html_safe, :multiple => nil) end def test_stringify_symbol_keys diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 82292cb..0b0e01a 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -344,7 +344,7 @@ class UrlHelperTest < ActionView::TestCase end def test_mail_to_with_img - assert_dom_equal %(), mail_to('feedback@example.com', '') + assert_dom_equal %(), mail_to('feedback@example.com', ''.html_safe) end def test_mail_to_with_hex -- 1.6.4.2 From 0307dbaba92d7981cd7d20ccca5fae0717f9e2b2 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 24 Feb 2010 22:28:40 -0200 Subject: [PATCH 507/779] add time_separator for minutes only if minutes aren't hidden Signed-off-by: Jeremy Kemper --- actionpack/lib/action_view/helpers/date_helper.rb | 2 +- actionpack/test/template/date_helper_test.rb | 41 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index add242d..29ccdb0 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -907,7 +907,7 @@ module ActionView when :hour (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator] when :minute - @options[:time_separator] + @options[:discard_minute] ? "" : @options[:time_separator] when :second @options[:include_seconds] ? @options[:time_separator] : "" end diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index fb51b67..7d3075d 100644 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -2173,6 +2173,47 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, datetime_select("post", "updated_at", :discard_year => true, :discard_month => true) end + def test_datetime_select_discard_hour + @post = Post.new + @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35) + + expected = %{\n" + expected << %{\n" + expected << %{\n" + + assert_dom_equal expected, datetime_select("post", "updated_at", :discard_hour => true) + end + + def test_datetime_select_discard_minute + @post = Post.new + @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35) + + expected = %{\n" + expected << %{\n" + expected << %{\n" + + expected << " — " + + expected << %{\n" + expected << %{\n} + + assert_dom_equal expected, datetime_select("post", "updated_at", :discard_minute => true) + end + def test_datetime_select_invalid_order @post = Post.new @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35) -- 1.6.4.2 From 5b4e7c3fa0a0daaf339f55cad028fe03feade223 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 24 Feb 2010 18:54:49 -0800 Subject: [PATCH 508/779] Missing fileutils require --- railties/test/rack_static_test.rb | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/railties/test/rack_static_test.rb b/railties/test/rack_static_test.rb index ad673f6..b1bcef9 100644 --- a/railties/test/rack_static_test.rb +++ b/railties/test/rack_static_test.rb @@ -2,6 +2,7 @@ require 'abstract_unit' require 'action_controller' require 'rails/rack' +require 'fileutils' class RackStaticTest < ActiveSupport::TestCase def setup -- 1.6.4.2 From 3a3fa7f817acf7478c5a363d7826a23655812dea Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 25 Feb 2010 10:54:36 -0800 Subject: [PATCH 509/779] Missed singleton_class --- .../core_ext/object/singleton_class.rb | 13 +++++++++++++ 1 files changed, 13 insertions(+), 0 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/object/singleton_class.rb diff --git a/activesupport/lib/active_support/core_ext/object/singleton_class.rb b/activesupport/lib/active_support/core_ext/object/singleton_class.rb new file mode 100644 index 0000000..8dee54e --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/singleton_class.rb @@ -0,0 +1,13 @@ +class Object + # Returns the object's singleton class. + def singleton_class + class << self + self + end + end unless respond_to?(:singleton_class) + + # class_eval on an object acts like singleton_class_eval. + def class_eval(*args, &block) + singleton_class.class_eval(*args, &block) + end +end -- 1.6.4.2 From 63a7ef0d745dac45829d63ed258b90ae0df7d593 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 25 Feb 2010 09:32:29 -0800 Subject: [PATCH 510/779] Use Object#singleton_class instead of #metaclass. Prefer Ruby's choice. --- activerecord/lib/active_record/dirty.rb | 2 +- activesupport/CHANGELOG | 2 ++ .../lib/active_support/core_ext/object.rb | 1 + .../active_support/core_ext/object/metaclass.rb | 11 ++++++----- .../test/core_ext/object_and_class_ext_test.rb | 14 +++++++++++++- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index f189651..ef0f3b8 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -167,7 +167,7 @@ module ActiveRecord module ClassMethods def self.extended(base) - base.metaclass.alias_method_chain(:alias_attribute, :dirty) + base.singleton_class.alias_method_chain(:alias_attribute, :dirty) end def alias_attribute_with_dirty(new_name, old_name) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 0e763ce..440c6e7 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *2.3.6 (pending)* +* Use Object#singleton_class instead of #metaclass. Prefer Ruby's choice. [Jeremy Kemper] + * JSON backend for YAJL. Preferred if available. #2666 [Brian Lopez] * Introduce String#html_safe for rails_xss plugin and forward-compatibility with Rails 3. [Michael Koziarski, Santiago Pastorino, José Ignacio Costa] diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index f140be3..65bc222 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -3,4 +3,5 @@ require 'active_support/core_ext/object/conversions' require 'active_support/core_ext/object/extending' require 'active_support/core_ext/object/instance_variables' require 'active_support/core_ext/object/metaclass' +require 'active_support/core_ext/object/singleton_class' require 'active_support/core_ext/object/misc' diff --git a/activesupport/lib/active_support/core_ext/object/metaclass.rb b/activesupport/lib/active_support/core_ext/object/metaclass.rb index 93fb0ad..4b36a24 100644 --- a/activesupport/lib/active_support/core_ext/object/metaclass.rb +++ b/activesupport/lib/active_support/core_ext/object/metaclass.rb @@ -1,13 +1,14 @@ +require 'active_support/deprecation' + class Object - # Get object's meta (ghost, eigenclass, singleton) class + # Get object's meta (ghost, eigenclass, singleton) class. + # + # Deprecated in favor of Object#singleton_class. def metaclass class << self self end end - # If class_eval is called on an object, add those methods to its metaclass - def class_eval(*args, &block) - metaclass.class_eval(*args, &block) - end + deprecate :metaclass => :singleton_class end 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 b6515e0..fcaaeb4 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -107,7 +107,7 @@ class ClassExtTest < Test::Unit::TestCase end end -class ObjectTests < Test::Unit::TestCase +class ObjectTests < ActiveSupport::TestCase def test_suppress_re_raises assert_raise(LoadError) { suppress(ArgumentError) {raise LoadError} } end @@ -176,6 +176,18 @@ class ObjectTests < Test::Unit::TestCase end assert_equal "bar", string.foo end + + def test_singleton_class + o = Object.new + assert_equal class << o; self end, o.singleton_class + end + + def test_metaclass_deprecated + o = Object.new + assert_deprecated /use singleton_class instead/ do + assert_equal o.singleton_class, o.metaclass + end + end end class ObjectInstanceVariableTest < Test::Unit::TestCase -- 1.6.4.2 From 268c9040d5c3c7ed30f3923eee71a78eeece8a8a Mon Sep 17 00:00:00 2001 From: Christoph Schiessl Date: Tue, 23 Feb 2010 18:49:11 +0100 Subject: [PATCH 511/779] remove rubygems (version >= 1.3.6) deprecation message by replacing Gem::Dependency#version_requirements with Gem::Dependency#requirement [#4026 state:committed] Signed-off-by: Prem Sichanugrist Signed-off-by: Jeremy Kemper --- railties/lib/rails/gem_dependency.rb | 15 +++++++++++---- railties/test/gem_dependency_test.rb | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb index 06d830b..8a496ca 100644 --- a/railties/lib/rails/gem_dependency.rb +++ b/railties/lib/rails/gem_dependency.rb @@ -83,7 +83,7 @@ module Rails specification.dependencies.reject do |dependency| dependency.type == :development end.map do |dependency| - GemDependency.new(dependency.name, :requirement => dependency.version_requirements) + GemDependency.new(dependency.name, :requirement => (dependency.respond_to?(:requirement) ? dependency.requirement : dependency.version_requirements)) end end @@ -115,9 +115,16 @@ module Rails @spec = s end - def requirement - r = version_requirements - (r == Gem::Requirement.default) ? nil : r + if method_defined?(:requirement) + def requirement + req = super + req unless req == Gem::Requirement.default + end + else + def requirement + req = version_requirements + req unless req == Gem::Requirement.default + end end def built? diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb index a4cf6f7..a165016 100644 --- a/railties/test/gem_dependency_test.rb +++ b/railties/test/gem_dependency_test.rb @@ -174,7 +174,7 @@ class GemDependencyTest < Test::Unit::TestCase def test_gem_from_directory_name dummy_gem = Rails::GemDependency.from_directory_name('dummy-gem-1.1', false) assert_equal 'dummy-gem', dummy_gem.name - assert_equal '= 1.1', dummy_gem.version_requirements.to_s + assert_equal '= 1.1', dummy_gem.requirement.to_s end def test_gem_from_directory_name_loads_specification_successfully -- 1.6.4.2 From 5695b1bdd9f883c28e0697af6983def9e786379c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 25 Feb 2010 15:12:08 -0800 Subject: [PATCH 512/779] Fill in for I18n.normalize_translation_keys removed from 0.3.4 --- activesupport/lib/active_support/vendor.rb | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index 75316a8..4e06d5b 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -26,3 +26,11 @@ rescue Gem::LoadError $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.3.3/lib" end require 'i18n' + +module I18n + if !respond_to?(:normalize_translation_keys) && respond_to?(:normalize_keys) + def self.normalize_translation_keys(*args) + normalize_keys(*args) + end + end +end -- 1.6.4.2 From b06e5dce9786bf69ac5f114286fc9236dc9bfe5c Mon Sep 17 00:00:00 2001 From: Tobias Bielohlawek Date: Tue, 8 Sep 2009 15:51:56 +0200 Subject: [PATCH 513/779] fixed a 'RecordNotFound' bug when calling 'reload' on a object which doesn't met the default_scope conditions, added test [#3166 status:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reload method didn't made use of 'with_exclusive_scope' when reloading the object. This lead to a RecordNotFound exception, in case the object doesn't met the default_scope condition (anymore) - which is obviously a bug. This quick fix makes use of with_exclusive_scope in the reload method as well. See test for full example. Signed-off-by: José Valim --- activerecord/lib/active_record/base.rb | 2 +- activerecord/test/cases/base_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 78c580f..7ca7a96 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2716,7 +2716,7 @@ module ActiveRecord #:nodoc: def reload(options = nil) clear_aggregation_cache clear_association_cache - @attributes.update(self.class.find(self.id, options).instance_variable_get('@attributes')) + @attributes.update(self.class.send(:with_exclusive_scope) { self.class.find(self.id, options) }.instance_variable_get('@attributes')) @attributes_cache = {} self end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 6f7a779..2bf7939 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1645,6 +1645,12 @@ class BasicsTest < ActiveRecord::TestCase assert_equal t1.title, t2.title end + def test_reload_with_exclusive_scope + dev = DeveloperCalledDavid.first + dev.update_attributes!( :name => "NotDavid" ) + assert_equal dev, dev.reload + end + def test_define_attr_method_with_value k = Class.new( ActiveRecord::Base ) k.send(:define_attr_method, :table_name, "foo") -- 1.6.4.2 From ed7322f33622023e7075b3738dc82ed3d78f3c9d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 26 Feb 2010 08:05:53 -0800 Subject: [PATCH 514/779] Deprecate toplevel plugins tasks in favor of lib/tasks for Rails 3 forward compat --- .../components/plugin/plugin_generator.rb | 4 ++-- railties/lib/tasks/rails.rb | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb b/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb index 6826998..34c10b5 100644 --- a/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb +++ b/railties/lib/rails_generator/generators/components/plugin/plugin_generator.rb @@ -13,7 +13,7 @@ class PluginGenerator < Rails::Generator::NamedBase m.class_collisions class_name m.directory "#{plugin_path}/lib" - m.directory "#{plugin_path}/tasks" + m.directory "#{plugin_path}/lib/tasks" m.directory "#{plugin_path}/test" m.template 'README', "#{plugin_path}/README" @@ -23,7 +23,7 @@ class PluginGenerator < Rails::Generator::NamedBase m.template 'install.rb', "#{plugin_path}/install.rb" m.template 'uninstall.rb', "#{plugin_path}/uninstall.rb" m.template 'plugin.rb', "#{plugin_path}/lib/#{file_name}.rb" - m.template 'tasks.rake', "#{plugin_path}/tasks/#{file_name}_tasks.rake" + m.template 'tasks.rake', "#{plugin_path}/lib/tasks/#{file_name}.rake" m.template 'unit_test.rb', "#{plugin_path}/test/#{file_name}_test.rb" m.template 'test_helper.rb', "#{plugin_path}/test/test_helper.rb" if @with_generator diff --git a/railties/lib/tasks/rails.rb b/railties/lib/tasks/rails.rb index 0600a05..626e94b 100644 --- a/railties/lib/tasks/rails.rb +++ b/railties/lib/tasks/rails.rb @@ -4,6 +4,11 @@ $VERBOSE = nil Dir["#{File.dirname(__FILE__)}/*.rake"].each { |ext| load ext } # Load any custom rakefile extensions -Dir["#{RAILS_ROOT}/vendor/plugins/*/tasks/**/*.rake"].sort.each { |ext| load ext } +deprecated_paths = Dir["#{RAILS_ROOT}/vendor/plugins/*/tasks/**/*.rake"].sort +if deprecated_paths.any? + plugins = deprecated_paths.map { |p| $1 if p =~ %r((vendor/plugins/[^/]+/tasks)) }.compact + ActiveSupport::Deprecation.warn "Rake tasks in #{plugins.to_sentence} are deprecated. Use lib/tasks instead." + deprecated_tasks.each { |ext| load ext } +end Dir["#{RAILS_ROOT}/vendor/plugins/*/lib/tasks/**/*.rake"].sort.each { |ext| load ext } Dir["#{RAILS_ROOT}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } -- 1.6.4.2 From f4ce042795c9e6adf5cb1595f31039d0123cc48e Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 26 Feb 2010 08:10:33 -0800 Subject: [PATCH 515/779] Add *.gem, pkg, and .bundle to .gitignore. Remove globs that should be in user's global ignores. --- .gitignore | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 5b3bd90..d5ad877 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +*.gem +pkg +.bundle debug.log doc/rdoc activeresource/doc @@ -15,6 +18,3 @@ railties/test/500.html railties/doc/guides/html/images railties/doc/guides/html/stylesheets railties/guides/output -*.rbc -*.swp -*.swo -- 1.6.4.2 From f56a1631be74d416625a436f4b96bba64b4d2855 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 26 Feb 2010 08:54:42 -0800 Subject: [PATCH 516/779] Typo: _tasks -> _paths --- railties/lib/tasks/rails.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/tasks/rails.rb b/railties/lib/tasks/rails.rb index 626e94b..b9bc099 100644 --- a/railties/lib/tasks/rails.rb +++ b/railties/lib/tasks/rails.rb @@ -8,7 +8,7 @@ deprecated_paths = Dir["#{RAILS_ROOT}/vendor/plugins/*/tasks/**/*.rake"].sort if deprecated_paths.any? plugins = deprecated_paths.map { |p| $1 if p =~ %r((vendor/plugins/[^/]+/tasks)) }.compact ActiveSupport::Deprecation.warn "Rake tasks in #{plugins.to_sentence} are deprecated. Use lib/tasks instead." - deprecated_tasks.each { |ext| load ext } + deprecated_paths.each { |ext| load ext } end Dir["#{RAILS_ROOT}/vendor/plugins/*/lib/tasks/**/*.rake"].sort.each { |ext| load ext } Dir["#{RAILS_ROOT}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } -- 1.6.4.2 From 1e64cdf8c9f4cbee8df978b1cbda051e0f180a37 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Mon, 1 Mar 2010 21:08:18 -0500 Subject: [PATCH 517/779] Bump version constants to 2.3.6 to more easily support generating prerelease gems --- actionmailer/Rakefile | 2 +- actionmailer/lib/action_mailer/version.rb | 2 +- actionpack/Rakefile | 2 +- actionpack/lib/action_pack/version.rb | 2 +- activerecord/Rakefile | 2 +- activerecord/lib/active_record/version.rb | 2 +- activeresource/Rakefile | 2 +- activeresource/lib/active_resource/version.rb | 2 +- activesupport/lib/active_support/version.rb | 2 +- railties/Rakefile | 10 +++++----- railties/lib/rails/version.rb | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index 2d099a9..07748ba 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -54,7 +54,7 @@ spec = Gem::Specification.new do |s| s.rubyforge_project = "actionmailer" s.homepage = "http://www.rubyonrails.org" - s.add_dependency('actionpack', '= 2.3.5' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.6' + PKG_BUILD) s.has_rdoc = true s.requirements << 'none' diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index a428905..08f67e2 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -2,7 +2,7 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 5 + TINY = 6 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 73f9efe..23e251f 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -79,7 +79,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.3.5' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.6' + PKG_BUILD) s.add_dependency('rack', '~> 1.0.0') s.require_path = 'lib' diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 4c88b3d..496f386 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -2,7 +2,7 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 5 + TINY = 6 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 2c5ad5c..05f0e23 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -192,7 +192,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.5' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.6' + PKG_BUILD) s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite" s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite" diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index d9a0fc2..f9c8c8e 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,7 +2,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 5 + TINY = 6 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activeresource/Rakefile b/activeresource/Rakefile index ee75836..6cc04eb 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -66,7 +66,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.5' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.6' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'active_resource' diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index 8833db9..d25c9a0 100644 --- a/activeresource/lib/active_resource/version.rb +++ b/activeresource/lib/active_resource/version.rb @@ -2,7 +2,7 @@ module ActiveResource module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 5 + TINY = 6 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 475a0a0..054c0ca 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -2,7 +2,7 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 5 + TINY = 6 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/railties/Rakefile b/railties/Rakefile index bf88f33..5654b90 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -314,11 +314,11 @@ spec = Gem::Specification.new do |s| EOF s.add_dependency('rake', '>= 0.8.3') - s.add_dependency('activesupport', '= 2.3.5' + PKG_BUILD) - s.add_dependency('activerecord', '= 2.3.5' + PKG_BUILD) - s.add_dependency('actionpack', '= 2.3.5' + PKG_BUILD) - s.add_dependency('actionmailer', '= 2.3.5' + PKG_BUILD) - s.add_dependency('activeresource', '= 2.3.5' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.6' + PKG_BUILD) + s.add_dependency('activerecord', '= 2.3.6' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.6' + PKG_BUILD) + s.add_dependency('actionmailer', '= 2.3.6' + PKG_BUILD) + s.add_dependency('activeresource', '= 2.3.6' + PKG_BUILD) s.rdoc_options << '--exclude' << '.' s.has_rdoc = false diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index 98e4e03..0fcaef8 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -2,7 +2,7 @@ module Rails module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 5 + TINY = 6 STRING = [MAJOR, MINOR, TINY].join('.') end -- 1.6.4.2 From 3969148a13aaafb57c2882d25dee620e29109cd5 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 2 Mar 2010 14:14:37 -0800 Subject: [PATCH 518/779] Deprecate omitting the leading slash on a path arg to assert_redirected_to --- .../assertions/response_assertions.rb | 27 +++++++++++++------ .../test/controller/action_pack_assertions_test.rb | 4 ++- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb index 931a031..f29eefc 100644 --- a/actionpack/lib/action_controller/assertions/response_assertions.rb +++ b/actionpack/lib/action_controller/assertions/response_assertions.rb @@ -144,16 +144,25 @@ module ActionController end def normalize_argument_to_redirection(fragment) - after_routing = @controller.url_for(fragment) - if after_routing =~ %r{^\w+://.*} - after_routing - else - # FIXME - this should probably get removed. - if after_routing.first != '/' - after_routing = '/' + after_routing + case fragment + when %r{^\w[\w\d+.-]*:.*} + fragment + when String + if fragment =~ %r{^\w[\w\d+.-]*:.*} + fragment + else + if fragment !~ /^\// + ActiveSupport::Deprecation.warn "Omitting the leading slash on a path with assert_redirected_to is deprecated. Use '/#{fragment}' instead.", caller(2) + fragment = "/#{fragment}" + end + @request.protocol + @request.host_with_port + fragment end - @request.protocol + @request.host_with_port + after_routing - end + when :back + raise RedirectBackError unless refer = @request.headers["Referer"] + refer + else + @controller.url_for(fragment) + end.gsub(/[\r\n]/, '') end end end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 6e92eff..ec7e216 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -458,7 +458,9 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_redirected_to_url_no_leadling_slash process :redirect_to_path - assert_redirected_to 'some/path' + assert_deprecated /leading/ do + assert_redirected_to 'some/path' + end end def test_redirected_to_url_full_url -- 1.6.4.2 From d3a815220345e9e0d7081c6d05bef55560466d41 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 5 Mar 2010 19:40:41 -0200 Subject: [PATCH 519/779] =?UTF-8?q?Adds=20disable=20option=20to=20date=5Fhelpers=20generated=20hidden=20fields=20when=20html=5Foptions=20specifies=20it.=20ht=20by=20Marc=20Sch=C3=BCtz?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#3807 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_view/helpers/date_helper.rb | 3 +- actionpack/test/template/date_helper_test.rb | 55 +++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 29ccdb0..f556488 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -1,5 +1,6 @@ require "date" require 'action_view/helpers/tag_helper' +require 'active_support/core_ext/hash/slice' module ActionView module Helpers @@ -865,7 +866,7 @@ module ActionView :id => input_id_from_type(type), :name => input_name_from_type(type), :value => value - }) + "\n").html_safe + }.merge(@html_options.slice(:disabled))) + "\n").html_safe end # Returns the name attribute for the input tag diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index 7d3075d..9a2d490 100644 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -1229,6 +1229,23 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, date_select("post", "written_on", :order => [ :month, :year ]) end + def test_date_select_without_day_and_with_disabled_html_option + @post = Post.new + @post.written_on = Date.new(2004, 6, 15) + + expected = "\n" + + expected << %{\n" + + expected << %{\n" + + assert_dom_equal expected, date_select("post", "written_on", { :order => [ :month, :year ] }, :disabled => true) + end + def test_date_select_within_fields_for @post = Post.new @post.written_on = Date.new(2004, 6, 15) @@ -1713,6 +1730,25 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, time_select("post", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute'}) end + def test_time_select_with_disabled_html_option + @post = Post.new + @post.written_on = Time.local(2004, 6, 15, 15, 16, 35) + + expected = %{\n} + expected << %{\n} + expected << %{\n} + + expected << %(\n" + expected << " : " + expected << %(\n" + + assert_dom_equal expected, time_select("post", "written_on", {}, :disabled => true) + end + def test_datetime_select @post = Post.new @post.updated_at = Time.local(2004, 6, 15, 16, 35) @@ -2173,6 +2209,25 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, datetime_select("post", "updated_at", :discard_year => true, :discard_month => true) end + def test_datetime_select_discard_year_and_month_with_disabled_html_option + @post = Post.new + @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35) + + expected = %{\n} + expected << %{\n} + expected << %{\n} + + expected << %{\n" + expected << " : " + expected << %{\n" + + assert_dom_equal expected, datetime_select("post", "updated_at", { :discard_year => true, :discard_month => true }, :disabled => true) + end + def test_datetime_select_discard_hour @post = Post.new @post.updated_at = Time.local(2004, 6, 15, 15, 16, 35) -- 1.6.4.2 From f6f75e84c5a186ea4c689dd8b7ae0da391c226ef Mon Sep 17 00:00:00 2001 From: Stijn Mathysen Date: Fri, 5 Mar 2010 20:04:57 -0200 Subject: [PATCH 520/779] Removed the + sign as an accepted character from the parameterize method, as a + sign is interpreted by the browser as a space, possibly resulting in a "ArgumentError: illegal character in key" [#4080 state:committed] Signed-off-by: Jeremy Kemper --- activesupport/lib/active_support/inflector.rb | 2 +- activesupport/test/inflector_test_cases.rb | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index 3ed30bd..71f4c44 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -260,7 +260,7 @@ module ActiveSupport # replace accented chars with ther ascii equivalents parameterized_string = transliterate(string) # Turn unwanted chars into the seperator - parameterized_string.gsub!(/[^a-z0-9\-_\+]+/i, sep) + parameterized_string.gsub!(/[^a-z0-9\-_]+/i, sep) unless sep.blank? re_sep = Regexp.escape(sep) # No more than one of the separator in a row. diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb index 584cbff..dbd9059 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -151,7 +151,8 @@ module InflectorTestCases "Allow_Under_Scores" => "allow_under_scores", "Trailing bad characters!@#" => "trailing-bad-characters", "!@#Leading bad characters" => "leading-bad-characters", - "Squeeze separators" => "squeeze-separators" + "Squeeze separators" => "squeeze-separators", + "Test with + sign" => "test-with-sign" } StringToParameterizeWithNoSeparator = { @@ -159,7 +160,8 @@ module InflectorTestCases "Random text with *(bad)* characters" => "randomtextwithbadcharacters", "Trailing bad characters!@#" => "trailingbadcharacters", "!@#Leading bad characters" => "leadingbadcharacters", - "Squeeze separators" => "squeezeseparators" + "Squeeze separators" => "squeezeseparators", + "Test with + sign" => "testwithsign" } StringToParameterizeWithUnderscore = { @@ -167,7 +169,8 @@ module InflectorTestCases "Random text with *(bad)* characters" => "random_text_with_bad_characters", "Trailing bad characters!@#" => "trailing_bad_characters", "!@#Leading bad characters" => "leading_bad_characters", - "Squeeze separators" => "squeeze_separators" + "Squeeze separators" => "squeeze_separators", + "Test with + sign" => "test_with_sign" } # Ruby 1.9 doesn't do Unicode normalization yet. -- 1.6.4.2 From 5a806f67591978a4f65af12ddc3595c7c356b49e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 6 Mar 2010 15:30:43 -0800 Subject: [PATCH 521/779] converting inject to each and map [#4119 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/base.rb | 9 ++++----- 1 files changed, 4 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7ca7a96..eed35e2 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2765,10 +2765,9 @@ module ActiveRecord #:nodoc: # Returns a hash of all the attributes with their names as keys and the values of the attributes as values. def attributes - self.attribute_names.inject({}) do |attrs, name| - attrs[name] = read_attribute(name) - attrs - end + attrs = {} + attribute_names.each { |name| attrs[name] = read_attribute(name) } + attrs end # Returns a hash of attributes before typecasting and deserialization. @@ -3129,7 +3128,7 @@ module ActiveRecord #:nodoc: # Returns a comma-separated pair list, like "key1 = val1, key2 = val2". def comma_pair_list(hash) - hash.inject([]) { |list, pair| list << "#{pair.first} = #{pair.last}" }.join(", ") + hash.map { |k,v| "#{k} = #{v}" }.join(", ") end def quoted_column_names(attributes = attributes_with_quotes) -- 1.6.4.2 From abb8fbde730f6411f6b58959e6105643bc0759de Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 9 Mar 2010 11:06:31 -0800 Subject: [PATCH 522/779] Disprefer JSONGem decoder since it only decodes JSON objects --- activesupport/lib/active_support/json/decoding.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index 2691caf..2182b90 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -6,7 +6,7 @@ module ActiveSupport module JSON # Listed in order of preference. - DECODERS = %w(Yajl JSONGem Yaml) + DECODERS = %w(Yajl Yaml) class << self attr_reader :parse_error -- 1.6.4.2 From cbc0201a3e0145da51baa9d228d88633033521ce Mon Sep 17 00:00:00 2001 From: Sam Ruby Date: Wed, 10 Mar 2010 10:04:54 -0500 Subject: [PATCH 523/779] Add deprecation warning for overwrite_params and remove rdoc [#4073 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_controller/base.rb | 9 --------- actionpack/lib/action_controller/url_rewriter.rb | 5 ++++- actionpack/test/controller/url_rewriter_test.rb | 16 ++++++++++------ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 4a6629b..62f4395 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -616,15 +616,6 @@ module ActionController #:nodoc: # displayed on: # # url_for :controller => 'posts', :action => nil - # - # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the - # :overwrite_params options. Say for your posts you have different views for showing and printing them. - # Then, in the show view, you get the URL for the print view like this - # - # url_for :overwrite_params => { :action => 'print' } - # - # This takes the current URL as is and only exchanges the action. In contrast, url_for :action => 'print' - # would have slashed-off the path components after the changed action. def url_for(options = {}) options ||= {} case options diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb index e4c2a29..13194bc 100644 --- a/actionpack/lib/action_controller/url_rewriter.rb +++ b/actionpack/lib/action_controller/url_rewriter.rb @@ -159,6 +159,9 @@ module ActionController end def rewrite(options = {}) + if options.include?(:overwrite_params) + ActiveSupport::Deprecation.warn 'The :overwrite_params option is deprecated. Specify all the necessary parameters instead', caller + end rewrite_url(options) end @@ -194,7 +197,7 @@ module ActionController options = options.symbolize_keys options.update(options[:params].symbolize_keys) if options[:params] - if (overwrite = options.delete(:overwrite_params)) + if overwrite = options.delete(:overwrite_params) options.update(@parameters.symbolize_keys) options.update(overwrite.symbolize_keys) end diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb index fdc4cfa..e9ae66b 100644 --- a/actionpack/test/controller/url_rewriter_test.rb +++ b/actionpack/test/controller/url_rewriter_test.rb @@ -65,9 +65,11 @@ class UrlRewriterTests < ActionController::TestCase @params[:action] = 'bye' @params[:id] = '2' - assert_equal '/hi/hi/2', @rewriter.rewrite(:only_path => true, :overwrite_params => {:action => 'hi'}) - u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:action => 'hi'}) - assert_match %r(/hi/hi/2$), u + assert_deprecated /overwrite_params/ do + assert_equal '/hi/hi/2', @rewriter.rewrite(:only_path => true, :overwrite_params => {:action => 'hi'}) + u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:action => 'hi'}) + assert_match %r(/hi/hi/2$), u + end end def test_overwrite_removes_original @@ -75,9 +77,11 @@ class UrlRewriterTests < ActionController::TestCase @params[:action] = 'list' @params[:list_page] = 1 - assert_equal '/search/list?list_page=2', @rewriter.rewrite(:only_path => true, :overwrite_params => {"list_page" => 2}) - u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:list_page => 2}) - assert_equal 'http://test.host/search/list?list_page=2', u + assert_deprecated /overwrite_params/ do + assert_equal '/search/list?list_page=2', @rewriter.rewrite(:only_path => true, :overwrite_params => {"list_page" => 2}) + u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:list_page => 2}) + assert_equal 'http://test.host/search/list?list_page=2', u + end end def test_to_str -- 1.6.4.2 From ec760a57d129295550c050a8ea0ce218e615ab15 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 11 Mar 2010 17:08:33 -0800 Subject: [PATCH 524/779] OutputBuffer#to_yaml should return string yaml, not some custom class dump --- .../core_ext/string/output_safety.rb | 4 ++++ activesupport/test/core_ext/string_ext_test.rb | 4 ++++ 2 files changed, 8 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index c200c72..66906bb 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -104,6 +104,10 @@ module ActiveSupport #:nodoc: def to_s self end + + def to_yaml + "".replace(self).to_yaml + end end end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index e2e6cbb..cc2ecf5 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -499,4 +499,8 @@ class OutputSafetyTest < ActiveSupport::TestCase assert_equal "hello".concat(13), string assert string.html_safe? end + + test 'emits normal string yaml' do + assert_equal 'foo'.to_yaml, 'foo'.html_safe.to_yaml + end end -- 1.6.4.2 From eed8a8863d7d5c038b5dd620d4b904ea62528afb Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 11 Mar 2010 17:32:26 -0800 Subject: [PATCH 525/779] Write strings to fragment cache, not outputbuffers --- .../lib/action_controller/caching/fragments.rb | 4 +++- .../core_ext/string/output_safety.rb | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 812238b..50be3e1 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -41,7 +41,9 @@ module ActionController #:nodoc: else pos = buffer.length block.call - write_fragment(name, buffer[pos..-1], options) + content = buffer[pos..-1] + content = content.as_str if content.respond_to?(:as_str) + write_fragment(name, content, options) end else block.call diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 66906bb..46d6cff 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -105,8 +105,12 @@ module ActiveSupport #:nodoc: self end + def as_str + ''.replace(self) + end + def to_yaml - "".replace(self).to_yaml + as_str.to_yaml end end end @@ -114,6 +118,10 @@ end class String alias_method :add_without_safety, :+ + def as_str + self + end + def html_safe ActiveSupport::SafeBuffer.new(self) end -- 1.6.4.2 From 40c393cb84206380c76dd2f5429215d32b09bc92 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 11 Mar 2010 18:37:28 -0800 Subject: [PATCH 526/779] Be sure to pass through args to to_yaml --- .../core_ext/string/output_safety.rb | 4 ++-- activesupport/test/core_ext/string_ext_test.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 46d6cff..a467efe 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -109,8 +109,8 @@ module ActiveSupport #:nodoc: ''.replace(self) end - def to_yaml - as_str.to_yaml + def to_yaml(*args) + as_str.to_yaml(*args) end end end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index cc2ecf5..2325103 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -501,6 +501,6 @@ class OutputSafetyTest < ActiveSupport::TestCase end test 'emits normal string yaml' do - assert_equal 'foo'.to_yaml, 'foo'.html_safe.to_yaml + assert_equal 'foo'.to_yaml, 'foo'.html_safe.to_yaml(:foo => 1) end end -- 1.6.4.2 From 056f957b222b8ec995e4ef7462e5097505da9377 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 14 Mar 2010 17:26:19 -0300 Subject: [PATCH 527/779] There's a Ruby issue with File.basename different versions returns different things, so we shouldn't test that [#4174] Signed-off-by: Jeremy Kemper --- actionpack/test/template/asset_tag_helper_test.rb | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 46e6129..a4e0d26 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -128,7 +128,6 @@ class AssetTagHelperTest < ActionView::TestCase ImageLinkToTag = { %(image_tag("xml.png")) => %(Xml), - %(image_tag("..jpg")) => %(), %(image_tag("rss.gif", :alt => "rss syndication")) => %(rss syndication), %(image_tag("gold.png", :size => "45x70")) => %(Gold), %(image_tag("gold.png", "size" => "45x70")) => %(Gold), -- 1.6.4.2 From 9b209e8cb89203b395622030bf526e3c3279722b Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 14 Mar 2010 18:55:13 -0700 Subject: [PATCH 528/779] read_ and write_fragment cache preserve html safety yet cache strings only --- .../lib/action_controller/caching/fragments.rb | 14 ++++++-------- actionpack/test/controller/caching_test.rb | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 50be3e1..6a3d1b9 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -41,9 +41,7 @@ module ActionController #:nodoc: else pos = buffer.length block.call - content = buffer[pos..-1] - content = content.as_str if content.respond_to?(:as_str) - write_fragment(name, content, options) + write_fragment(name, buffer[pos..-1], options) end else block.call @@ -54,9 +52,9 @@ module ActionController #:nodoc: def write_fragment(key, content, options = nil) return content unless cache_configured? - key = fragment_cache_key(key) - self.class.benchmark "Cached fragment miss: #{key}" do + key = fragment_cache_key(key) + content = content.html_safe.as_str if content.respond_to?(:html_safe) cache_store.write(key, content, options) end @@ -67,10 +65,10 @@ module ActionController #:nodoc: def read_fragment(key, options = nil) return unless cache_configured? - key = fragment_cache_key(key) - self.class.benchmark "Cached fragment hit: #{key}" do - cache_store.read(key, options) + key = fragment_cache_key(key) + result = cache_store.read(key, options) + result.respond_to?(:html_safe) ? result.html_safe : result end end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index af142c5..d67e31f 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -621,6 +621,20 @@ class FragmentCachingTest < ActionController::TestCase assert !fragment_computed assert_equal 'generated till now -> fragment content', buffer end + + def test_html_safety + assert_nil @store.read('views/name') + content = 'value'.html_safe + assert_equal content, @controller.write_fragment('name', content) + + cached = @store.read('views/name') + assert_equal content, cached + assert_equal String, cached.class + + html_safe = @controller.read_fragment('name') + assert_equal content, html_safe + assert html_safe.html_safe? + end end class FunctionalCachingController < ActionController::Base -- 1.6.4.2 From b99914cc3e58e6444f66006cd4b9b4fa7368afe2 Mon Sep 17 00:00:00 2001 From: Bas Van Klinkenberg Date: Sat, 1 Aug 2009 02:24:40 +0200 Subject: [PATCH 529/779] Fixed a bug in JSON decoding with Yaml backend, where a combination of dates, escaped or unicode encoded data and arrays would make the parser fail with a ParseError exception. [#2831] Signed-off-by: Yehuda Katz Signed-off-by: Jeremy Kemper --- .../lib/active_support/json/backends/yaml.rb | 17 ++++++++++------- activesupport/test/json/decoding_test.rb | 8 +++++++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb index ab2ec46..4c41042 100644 --- a/activesupport/lib/active_support/json/backends/yaml.rb +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -27,11 +27,9 @@ module ActiveSupport pos = scanner.pos elsif quoting == char if json[pos..scanner.pos-2] =~ DATE_REGEX - # found a date, track the exact positions of the quotes so we can remove them later. - # oh, and increment them for each current mark, each one is an extra padded space that bumps - # the position in the final YAML output - total_marks = marks.size - times << pos+total_marks << scanner.pos+total_marks + # found a date, track the exact positions of the quotes so we can + # overwrite them with spaces later. + times << pos << scanner.pos end quoting = false end @@ -59,7 +57,12 @@ module ActiveSupport output = [] left_pos.each_with_index do |left, i| scanner.pos = left.succ - output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do + chunk = scanner.peek(right_pos[i] - scanner.pos + 1) + # overwrite the quotes found around the dates with spaces + while times.size > 0 && times[0] <= right_pos[i] + chunk[times.shift - scanner.pos - 1] = ' ' + end + chunk.gsub!(/\\([\\\/]|u[[:xdigit:]]{4})/) do ustr = $1 if ustr.start_with?('u') [ustr[1..-1].to_i(16)].pack("U") @@ -69,10 +72,10 @@ module ActiveSupport ustr end end + output << chunk end output = output * " " - times.each { |i| output[i-1] = ' ' } output.gsub!(/\\\//, '/') output end diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index a241767..e45851e 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -32,7 +32,13 @@ class TestJSONDecoding < ActiveSupport::TestCase %q({"a": "\u003cunicode\u0020escape\u003e"}) => {"a" => ""}, %q({"a": "\\\\u0020skip double backslashes"}) => {"a" => "\\u0020skip double backslashes"}, %q({"a": "\u003cbr /\u003e"}) => {'a' => "
"}, - %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["","",""]} + %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["","",""]}, + # test combination of dates and escaped or unicode encoded data in arrays + %q([{"d":"1970-01-01", "s":"\u0020escape"},{"d":"1970-01-01", "s":"\u0020escape"}]) => + [{'d' => Date.new(1970, 1, 1), 's' => ' escape'},{'d' => Date.new(1970, 1, 1), 's' => ' escape'}], + %q([{"d":"1970-01-01","s":"http:\/\/example.com"},{"d":"1970-01-01","s":"http:\/\/example.com"}]) => + [{'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'}, + {'d' => Date.new(1970, 1, 1), 's' => 'http://example.com'}] } # load the default JSON backend -- 1.6.4.2 From b3d32a5b28e96cda5ef66eb165325e08c57bdc74 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 15 Mar 2010 11:17:18 -0700 Subject: [PATCH 530/779] to_str works here --- .../lib/action_controller/caching/fragments.rb | 2 +- .../core_ext/string/output_safety.rb | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 6a3d1b9..eefb649 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -54,7 +54,7 @@ module ActionController #:nodoc: self.class.benchmark "Cached fragment miss: #{key}" do key = fragment_cache_key(key) - content = content.html_safe.as_str if content.respond_to?(:html_safe) + content = content.html_safe.to_str if content.respond_to?(:html_safe) cache_store.write(key, content, options) end diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index a467efe..95ed5d8 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -105,12 +105,8 @@ module ActiveSupport #:nodoc: self end - def as_str - ''.replace(self) - end - def to_yaml(*args) - as_str.to_yaml(*args) + to_str.to_yaml(*args) end end end -- 1.6.4.2 From ebf300f41b71d05f9d64d0ba71978158f9c797a3 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 15 Mar 2010 19:56:49 -0300 Subject: [PATCH 531/779] object_and_class_ext_test warnings removed Signed-off-by: Jeremy Kemper --- .../test/core_ext/object_and_class_ext_test.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) 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 fcaaeb4..f26c126 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -171,7 +171,7 @@ class ObjectTests < ActiveSupport::TestCase def test_metaclass string = "Hello" - string.metaclass.instance_eval do + string.singleton_class.instance_eval do define_method(:foo) { "bar" } end assert_equal "bar", string.foo @@ -184,7 +184,7 @@ class ObjectTests < ActiveSupport::TestCase def test_metaclass_deprecated o = Object.new - assert_deprecated /use singleton_class instead/ do + assert_deprecated(/use singleton_class instead/) do assert_equal o.singleton_class, o.metaclass end end -- 1.6.4.2 From 374e49b467ec480ebeeae4280d412f44094a9870 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 8 Mar 2010 19:52:42 -0200 Subject: [PATCH 532/779] Change array entries to safe doesn't worth then the array is joined as a string losing the safe property of his entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_view/helpers/translation_helper.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index afa592d..e44a985 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -13,7 +13,7 @@ module ActionView def translate(key, options = {}) options[:raise] = true translation = I18n.translate(scope_key_by_partial(key), options) - translation.is_a?(Array) ? translation.map { |entry| entry.html_safe } : translation.html_safe + translation.respond_to?(:html_safe) ? translation.html_safe : translation rescue I18n::MissingTranslationData => e keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope]) content_tag('span', keys.join(', '), :class => 'translation_missing') -- 1.6.4.2 From 2d3c58068c2f35e0a63d67a11685d1e5057412ff Mon Sep 17 00:00:00 2001 From: Manfred Stienstra Date: Mon, 2 Nov 2009 21:06:12 +0100 Subject: [PATCH 533/779] Improve performance of Multibyte::Utils. Replace explicit for-loops by faster enumeration methods. [#3158] Signed-off-by: Jeremy Kemper --- .../lib/active_support/multibyte/utils.rb | 13 ++++++------- 1 files changed, 6 insertions(+), 7 deletions(-) diff --git a/activesupport/lib/active_support/multibyte/utils.rb b/activesupport/lib/active_support/multibyte/utils.rb index 8e47763..b243df4 100644 --- a/activesupport/lib/active_support/multibyte/utils.rb +++ b/activesupport/lib/active_support/multibyte/utils.rb @@ -26,11 +26,11 @@ module ActiveSupport #:nodoc: else def self.verify(string) if expression = valid_character - for c in string.split(//) - return false unless expression.match(c) - end + # Splits the string on character boundaries, which are determined based on $KCODE. + string.split(//).all? { |c| expression.match(c) } + else + true end - true end end @@ -49,9 +49,8 @@ module ActiveSupport #:nodoc: else def self.clean(string) if expression = valid_character - stripped = []; for c in string.split(//) - stripped << c if expression.match(c) - end; stripped.join + # Splits the string on character boundaries, which are determined based on $KCODE. + string.split(//).grep(expression).join else string end -- 1.6.4.2 From 27aa22826cdd1804c2300716c2a19ebefdad3e33 Mon Sep 17 00:00:00 2001 From: James Golick Date: Tue, 16 Mar 2010 09:49:25 -0700 Subject: [PATCH 534/779] Improve performance of multibyte utils. Switch from using String#match to using String#=~. There's no need to generate a MatchData for each iteration since we're not using it. Signed-off-by: Jeremy Kemper --- .../lib/active_support/multibyte/utils.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/multibyte/utils.rb b/activesupport/lib/active_support/multibyte/utils.rb index b243df4..94b393c 100644 --- a/activesupport/lib/active_support/multibyte/utils.rb +++ b/activesupport/lib/active_support/multibyte/utils.rb @@ -27,7 +27,7 @@ module ActiveSupport #:nodoc: def self.verify(string) if expression = valid_character # Splits the string on character boundaries, which are determined based on $KCODE. - string.split(//).all? { |c| expression.match(c) } + string.split(//).all? { |c| expression =~ c } else true end -- 1.6.4.2 From 2310aef29be306704c0361f1188200f71df657df Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 16 Mar 2010 17:08:09 -0300 Subject: [PATCH 535/779] translation method of TranslationHelper module returns always SafeBuffer [#4194 status:resolved] Signed-off-by: Jeremy Kemper --- .../lib/action_view/helpers/translation_helper.rb | 2 +- .../test/fixtures/test/array_translation.erb | 1 + .../test/template/translation_helper_test.rb | 9 ++++++++- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 actionpack/test/fixtures/test/array_translation.erb diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index e44a985..185009c 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -13,7 +13,7 @@ module ActionView def translate(key, options = {}) options[:raise] = true translation = I18n.translate(scope_key_by_partial(key), options) - translation.respond_to?(:html_safe) ? translation.html_safe : translation + (translation.respond_to?(:join) ? translation.join : translation).html_safe rescue I18n::MissingTranslationData => e keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope]) content_tag('span', keys.join(', '), :class => 'translation_missing') diff --git a/actionpack/test/fixtures/test/array_translation.erb b/actionpack/test/fixtures/test/array_translation.erb new file mode 100644 index 0000000..12c0763 --- /dev/null +++ b/actionpack/test/fixtures/test/array_translation.erb @@ -0,0 +1 @@ +<%= t(['foo', 'bar']) %> \ No newline at end of file diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index 0a9efc7..d168a60 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -20,7 +20,14 @@ class TranslationHelperTest < Test::Unit::TestCase def test_translation_of_an_array I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"]) - assert_equal ["foo", "bar"], translate(["foo", "bar"]) + assert_equal "foobar", translate(["foo", "bar"]) + end + + def test_translation_of_an_array_with_html + expected = 'foobar' + I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(['foo', 'bar']) + @view = ActionView::Base.new(ActionController::Base.view_paths, {}) + assert_equal expected, @view.render(:file => "test/array_translation") end def test_delegates_localize_to_i18n -- 1.6.4.2 From 9cfa87519d841dabeb7f9887e88850db27298fd0 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 16 Mar 2010 19:59:04 -0300 Subject: [PATCH 536/779] scope_key_by_partial fix for Ruby 1.9 when there's virtual_path [#4202 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/action_view/helpers/translation_helper.rb | 5 +++-- .../fixtures/test/scoped_array_translation.erb | 1 + .../test/template/translation_helper_test.rb | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 actionpack/test/fixtures/test/scoped_array_translation.erb diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 185009c..9296622 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -29,8 +29,9 @@ module ActionView private def scope_key_by_partial(key) - if (key.respond_to?(:join) ? key.join : key.to_s).first == "." - template.path_without_format_and_extension.gsub(%r{/_?}, ".") + key.to_s + strkey = key.respond_to?(:join) ? key.join : key.to_s + if strkey.first == "." + template.path_without_format_and_extension.gsub(%r{/_?}, ".") + strkey else key end diff --git a/actionpack/test/fixtures/test/scoped_array_translation.erb b/actionpack/test/fixtures/test/scoped_array_translation.erb new file mode 100644 index 0000000..0a0c79f --- /dev/null +++ b/actionpack/test/fixtures/test/scoped_array_translation.erb @@ -0,0 +1 @@ +<%= t(['.foo', '.bar']) %> \ No newline at end of file diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index d168a60..24640e4 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -41,4 +41,10 @@ class TranslationHelperTest < Test::Unit::TestCase I18n.expects(:translate).with("people.index.foo", :locale => 'en', :raise => true).returns("") translate ".foo", :locale => 'en' end + + def test_scoping_by_partial_of_an_array + I18n.expects(:translate).with("test.scoped_array_translation.foo.bar", :raise => true).returns(["foo", "bar"]) + @view = ActionView::Base.new(ActionController::Base.view_paths, {}) + assert_equal "foobar", @view.render(:file => "test/scoped_array_translation") + end end -- 1.6.4.2 From f175d19e2afa3a7a642ac38591596a7b550f4329 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 16 Mar 2010 17:55:25 -0700 Subject: [PATCH 537/779] Bundler returns a Pathname from #loaded_from; work around it --- railties/lib/rails/vendor_gem_source_index.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/rails/vendor_gem_source_index.rb b/railties/lib/rails/vendor_gem_source_index.rb index 5b7721f..606011e 100644 --- a/railties/lib/rails/vendor_gem_source_index.rb +++ b/railties/lib/rails/vendor_gem_source_index.rb @@ -37,7 +37,7 @@ module Rails # handle vendor Rails gems - they are identified by having loaded_from set to "" # we add them manually to the list, so that other gems can find them via dependencies Gem.loaded_specs.each do |n, s| - next unless s.loaded_from.empty? + next if s.loaded_from.present? vendor_gems[s.full_name] = s end -- 1.6.4.2 From c0137f62d438d9112b84a69c857551566786d7a8 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 16 Mar 2010 19:45:00 -0700 Subject: [PATCH 538/779] Don't rely on Active Support being loaded here --- railties/lib/rails/vendor_gem_source_index.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/rails/vendor_gem_source_index.rb b/railties/lib/rails/vendor_gem_source_index.rb index 606011e..91f6167 100644 --- a/railties/lib/rails/vendor_gem_source_index.rb +++ b/railties/lib/rails/vendor_gem_source_index.rb @@ -37,7 +37,7 @@ module Rails # handle vendor Rails gems - they are identified by having loaded_from set to "" # we add them manually to the list, so that other gems can find them via dependencies Gem.loaded_specs.each do |n, s| - next if s.loaded_from.present? + next unless s.loaded_from.to_s.empty? vendor_gems[s.full_name] = s end -- 1.6.4.2 From 26f2cce232d5455231255fcc908a343c03f7929c Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Mon, 8 Mar 2010 22:57:47 +0100 Subject: [PATCH 539/779] button_to should generate an html_safe string Signed-off-by: Michael Koziarski --- actionpack/lib/action_view/helpers/url_helper.rb | 4 ++-- actionpack/test/template/url_helper_test.rb | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index bbe0473..74cb448 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -308,8 +308,8 @@ module ActionView html_options.merge!("type" => "submit", "value" => name) - "
" + - method_tag + tag("input", html_options) + request_token_tag + "
".html_safe + ("
" + + method_tag + tag("input", html_options) + request_token_tag + "
").html_safe end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 0b0e01a..cf89829 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -88,6 +88,10 @@ class UrlHelperTest < ActionView::TestCase ) end + def test_button_to_returns_an_html_safe_string + assert button_to("Hello", "http://www.example.com").html_safe? + end + def test_link_tag_with_straight_url assert_dom_equal "Hello", link_to("Hello", "http://www.example.com") end -- 1.6.4.2 From 629afe9f192bcb49886ca8e8b14397b3c88b7207 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 19 Mar 2010 22:26:57 -0300 Subject: [PATCH 540/779] ActionView::SafeBuffer should be there for backwards compatibility [#4241 state:committed] Signed-off-by: Michael Koziarski --- actionpack/lib/action_view.rb | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index e1adb48..9b7d0c4 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -53,5 +53,6 @@ end require 'active_support/core_ext/string/output_safety' +ActionView::SafeBuffer = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActionView::SafeBuffer', 'ActiveSupport::SafeBuffer') I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml" -- 1.6.4.2 From 97e07a88feaf334d7eaa44f66bcd57c08cd587a6 Mon Sep 17 00:00:00 2001 From: Tom Lea Date: Thu, 9 Jul 2009 16:13:22 +0100 Subject: [PATCH 541/779] Fix incorrect relative paths being used when looking up templates. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bug will manifest itself by failing to locate templates when running tests, or when running as a daemon (from /). It relates the the different behavior of ActionView::Template::Path#to_s and ActionView::Template::Path#to_str when a RAILS_ROOT is defined. #to_s reports a path relative to the root, and #to_str reports an absolute path. Signed-off-by: José Valim --- actionmailer/lib/action_mailer/base.rb | 2 +- actionmailer/test/mail_service_test.rb | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 36122e2..c275d5a 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -593,7 +593,7 @@ module ActionMailer #:nodoc: end def template_path - "#{template_root}/#{mailer_name}" + File.join(template_root, mailer_name) end def initialize_template_class(assigns) diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 78f3c9e..3b464df 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -861,6 +861,26 @@ EOF assert_equal "text/yaml", mail.parts[2].content_type end + def test_implicitly_path_when_running_from_none_rails_root + exected_path = File.expand_path(File.join(File.dirname(__FILE__), "fixtures", "test_mailer")) + with_a_rails_root do + Dir.chdir "/" do + template_path = TestMailer.allocate.send(:template_path) + assert_equal exected_path, File.expand_path(template_path) + end + end + end + + + def test_implicitly_multipart_messages_run_from_another_location_with_a_rails_root + with_a_rails_root do + Dir.chdir "/" do + mail = TestMailer.create_implicitly_multipart_example(@recipient) + assert_equal 3, mail.parts.length + end + end + end + def test_implicitly_multipart_messages_with_charset mail = TestMailer.create_implicitly_multipart_example(@recipient, 'iso-8859-1') @@ -1009,6 +1029,16 @@ EOF ensure ActionMailer::Base.smtp_settings[:enable_starttls_auto] = true end + +private + def with_a_rails_root + old_root = ::RAILS_ROOT if defined? ::RAILS_ROOT + Object.const_set(:RAILS_ROOT, File.join(File.dirname(__FILE__))) + yield + ensure + Object.send(:remove_const, :RAILS_ROOT) if defined? ::RAILS_ROOT + Object.const_set(:RAILS_ROOT, old_root) if old_root + end end class InheritableTemplateRootTest < Test::Unit::TestCase -- 1.6.4.2 From 0022fa309b6dd88d4019d1a94e6cd89866695349 Mon Sep 17 00:00:00 2001 From: Rodrigo Kochenburger Date: Mon, 22 Mar 2010 16:16:18 -0700 Subject: [PATCH 542/779] Set mailer template_root as absolute path [#2263 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionmailer/test/abstract_unit.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 9728ae5..0aed45b 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -17,7 +17,7 @@ ActionView::Template.register_template_handler :bak, lambda { |template| "Lame b $:.unshift "#{File.dirname(__FILE__)}/fixtures/helpers" ActionView::Base.cache_template_loading = true -FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') +FIXTURE_LOAD_PATH = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures')) ActionMailer::Base.template_root = FIXTURE_LOAD_PATH class MockSMTP -- 1.6.4.2 From ac7b5a23ba44bb28757a569baad8b70a5dca61f5 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 25 Mar 2010 11:46:23 -0300 Subject: [PATCH 543/779] Allow deprecation messages with or without a final period. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/deprecation.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 151ae5a..36289cf 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -52,7 +52,8 @@ module ActiveSupport private def deprecation_message(callstack, message = nil) message ||= "You are using deprecated behavior which will be removed from the next major or minor release." - "DEPRECATION WARNING: #{message}. #{deprecation_caller_message(callstack)}" + message += '.' unless message =~ /\.$/ + "DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}" end def deprecation_caller_message(callstack) -- 1.6.4.2 From 157c1808b9365cfc6a32e4dad9797c87aef3d83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 26 Mar 2010 20:21:07 +0100 Subject: [PATCH 544/779] Added compatibility to Ruby 1.9.2. --- railties/bin/rails | 11 ++++++----- railties/lib/rails_generator.rb | 7 +++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/railties/bin/rails b/railties/bin/rails index 6a0c675..728d054 100755 --- a/railties/bin/rails +++ b/railties/bin/rails @@ -1,19 +1,20 @@ -require File.dirname(__FILE__) + '/../lib/ruby_version_check' +$:.unshift File.expand_path("../../lib", __FILE__) + +require 'ruby_version_check' Signal.trap("INT") { puts; exit } -require File.dirname(__FILE__) + '/../lib/rails/version' +require 'rails/version' if %w(--version -v).include? ARGV.first puts "Rails #{Rails::VERSION::STRING}" exit(0) end freeze = ARGV.any? { |option| %w(--freeze -f).include?(option) } - app_path = ARGV.first -require File.dirname(__FILE__) + '/../lib/rails_generator' - +require 'rails_generator' require 'rails_generator/scripts/generate' + Rails::Generator::Base.use_application_sources! Rails::Generator::Scripts::Generate.new.run(ARGV, :generator => 'app') diff --git a/railties/lib/rails_generator.rb b/railties/lib/rails_generator.rb index 9f0ffc1..264c649 100644 --- a/railties/lib/rails_generator.rb +++ b/railties/lib/rails_generator.rb @@ -21,8 +21,11 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -$:.unshift(File.dirname(__FILE__)) -$:.unshift(File.dirname(__FILE__) + "/../../activesupport/lib") +railties = File.expand_path("..", __FILE__) +$:.unshift(railties) unless $:.include?(railties) + +activesupport = File.expand_path("../../../activesupport/lib", __FILE__) +$:.unshift(activesupport) unless $:.include?(activesupport) begin require 'active_support' -- 1.6.4.2 From c1b22000856412eb0f5966e9e2fbcc4d91b60208 Mon Sep 17 00:00:00 2001 From: Joe Rafaniello Date: Thu, 21 Jan 2010 16:56:04 -0500 Subject: [PATCH 545/779] Marshaling a time object added an instance variable to the object which affected the quoting of serialized attributes because the to_yaml of the original object did not match the to_yaml of the marshaled one. Also, Marshal.dump was modifying the source object which the client may not be aware of. Signed-off-by: wycats --- activesupport/lib/active_support/core_ext/time.rb | 4 ++-- activesupport/test/core_ext/time_ext_test.rb | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb index 78bbfc9..a8feb1a 100644 --- a/activesupport/lib/active_support/core_ext/time.rb +++ b/activesupport/lib/active_support/core_ext/time.rb @@ -15,14 +15,14 @@ class Time alias_method :_original_load, :_load def _load(marshaled_time) time = _original_load(marshaled_time) - utc = time.instance_variable_get('@marshal_with_utc_coercion') + utc = time.send(:remove_instance_variable, '@marshal_with_utc_coercion') if time.instance_variable_defined?('@marshal_with_utc_coercion') utc ? time.utc : time end end alias_method :_original_dump, :_dump def _dump(*args) - obj = self.frozen? ? self.dup : self + obj = self.dup obj.instance_variable_set('@marshal_with_utc_coercion', utc?) obj._original_dump(*args) end diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index a8005a1..ff220d4 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -745,4 +745,19 @@ class TimeExtMarshalingTest < Test::Unit::TestCase assert_equal t, unmarshaled assert_equal t.zone, unmarshaled.zone end + + def test_marshaling_does_not_modify_source_object + t = Time.local(2000) + Marshal.dump t + assert_equal false, t.instance_variable_defined?('@marshal_with_utc_coercion') + end + + def test_marshaling_does_not_affect_yaml_dump + t = Time.local(2000) + t2 = t.dup + marshaled = Marshal.dump t2 + unmarshaled = Marshal.load marshaled + assert_equal t.to_yaml, unmarshaled.to_yaml + end + end -- 1.6.4.2 From cf7ed7cf2dcd552d3b9e1076267ec0cc4c97564b Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 28 Mar 2010 17:53:16 -0300 Subject: [PATCH 546/779] Time marshalling backported [#4286 state:committed] Signed-off-by: wycats --- activesupport/lib/active_support/core_ext/time.rb | 12 ++++++++---- 1 files changed, 8 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb index a8feb1a..61e4894 100644 --- a/activesupport/lib/active_support/core_ext/time.rb +++ b/activesupport/lib/active_support/core_ext/time.rb @@ -15,14 +15,18 @@ class Time alias_method :_original_load, :_load def _load(marshaled_time) time = _original_load(marshaled_time) - utc = time.send(:remove_instance_variable, '@marshal_with_utc_coercion') if time.instance_variable_defined?('@marshal_with_utc_coercion') - utc ? time.utc : time + time.instance_eval do + if defined?(@marshal_with_utc_coercion) + val = remove_instance_variable("@marshal_with_utc_coercion") + end + val ? utc : self + end end end - + alias_method :_original_dump, :_dump def _dump(*args) - obj = self.dup + obj = dup obj.instance_variable_set('@marshal_with_utc_coercion', utc?) obj._original_dump(*args) end -- 1.6.4.2 From 0f18904e2b9b9f43ff439feedd1d74512be89861 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 27 Mar 2010 11:54:22 -0300 Subject: [PATCH 547/779] flatten not needed here Signed-off-by: wycats --- .../core_ext/class/attribute_accessors.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb index c795871..9b4a8f9 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb @@ -8,7 +8,7 @@ # Person.hair_colors = [:brown, :black, :blonde, :red] class Class def cattr_reader(*syms) - syms.flatten.each do |sym| + syms.each do |sym| next if sym.is_a?(Hash) class_eval(<<-EOS, __FILE__, __LINE__) unless defined? @@#{sym} # unless defined? @@hair_colors @@ -28,7 +28,7 @@ class Class def cattr_writer(*syms) options = syms.extract_options! - syms.flatten.each do |sym| + syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__) unless defined? @@#{sym} # unless defined? @@hair_colors @@#{sym} = nil # @@hair_colors = nil -- 1.6.4.2 From bc2af911f9f85191b573975c8421591d6f68c567 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 27 Mar 2010 12:10:13 -0300 Subject: [PATCH 548/779] backport of inconsistency with cattr_reader and matter_reader with some tweaks Signed-off-by: wycats --- .../core_ext/class/attribute_accessors.rb | 59 +++++++++++--------- .../core_ext/module/attribute_accessors.rb | 59 +++++++++++--------- .../test/core_ext/class/attribute_accessor_test.rb | 9 +++- .../core_ext/module/attribute_accessor_test.rb | 8 ++- 4 files changed, 81 insertions(+), 54 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb index 9b4a8f9..7ccf8b0 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb @@ -8,42 +8,49 @@ # Person.hair_colors = [:brown, :black, :blonde, :red] class Class def cattr_reader(*syms) + options = syms.extract_options! syms.each do |sym| next if sym.is_a?(Hash) - class_eval(<<-EOS, __FILE__, __LINE__) - unless defined? @@#{sym} # unless defined? @@hair_colors - @@#{sym} = nil # @@hair_colors = nil - end # end - # - def self.#{sym} # def self.hair_colors - @@#{sym} # @@hair_colors - end # end - # - def #{sym} # def hair_colors - @@#{sym} # @@hair_colors - end # end + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + unless defined? @@#{sym} + @@#{sym} = nil + end + + def self.#{sym} + @@#{sym} + end EOS + + unless options[:instance_reader] == false + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{sym} + @@#{sym} + end + EOS + end end end def cattr_writer(*syms) options = syms.extract_options! syms.each do |sym| - class_eval(<<-EOS, __FILE__, __LINE__) - unless defined? @@#{sym} # unless defined? @@hair_colors - @@#{sym} = nil # @@hair_colors = nil - end # end - # - def self.#{sym}=(obj) # def self.hair_colors=(obj) - @@#{sym} = obj # @@hair_colors = obj - end # end - # - #{" # - def #{sym}=(obj) # def hair_colors=(obj) - @@#{sym} = obj # @@hair_colors = obj - end # end - " unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + unless defined? @@#{sym} + @@#{sym} = nil + end + + def self.#{sym}=(obj) + @@#{sym} = obj + end EOS + + unless options[:instance_writer] == false + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{sym}=(obj) + @@#{sym} = obj + end + EOS + end end end diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb index 9359b22..54a29c0 100644 --- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb @@ -14,42 +14,49 @@ require "active_support/core_ext/array" # AppConfiguration.google_api_key = "overriding the api key!" class Module def mattr_reader(*syms) + options = syms.extract_options! syms.each do |sym| next if sym.is_a?(Hash) - class_eval(<<-EOS, __FILE__, __LINE__) - unless defined? @@#{sym} # unless defined? @@pagination_options - @@#{sym} = nil # @@pagination_options = nil - end # end - # - def self.#{sym} # def self.pagination_options - @@#{sym} # @@pagination_options - end # end - # - def #{sym} # def pagination_options - @@#{sym} # @@pagination_options - end # end + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + unless defined? @@#{sym} + @@#{sym} = nil + end + + def self.#{sym} + @@#{sym} + end EOS + + unless options[:instance_reader] == false + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{sym} + @@#{sym} + end + EOS + end end end def mattr_writer(*syms) options = syms.extract_options! syms.each do |sym| - class_eval(<<-EOS, __FILE__, __LINE__) - unless defined? @@#{sym} # unless defined? @@pagination_options - @@#{sym} = nil # @@pagination_options = nil - end # end - # - def self.#{sym}=(obj) # def self.pagination_options=(obj) - @@#{sym} = obj # @@pagination_options = obj - end # end - # - #{" # - def #{sym}=(obj) # def pagination_options=(obj) - @@#{sym} = obj # @@pagination_options = obj - end # end - " unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + unless defined? @@#{sym} + @@#{sym} = nil + end + + def self.#{sym}=(obj) + @@#{sym} = obj + end EOS + + unless options[:instance_writer] == false + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{sym}=(obj) + @@#{sym} = obj + end + EOS + end end end diff --git a/activesupport/test/core_ext/class/attribute_accessor_test.rb b/activesupport/test/core_ext/class/attribute_accessor_test.rb index 85d0dd8..d6bb1a6 100644 --- a/activesupport/test/core_ext/class/attribute_accessor_test.rb +++ b/activesupport/test/core_ext/class/attribute_accessor_test.rb @@ -4,7 +4,8 @@ class ClassAttributeAccessorTest < Test::Unit::TestCase def setup @class = Class.new do cattr_accessor :foo - cattr_accessor :bar, :instance_writer => false + cattr_accessor :bar, :instance_writer => false + cattr_reader :shaq, :instance_reader => false end @object = @class.new end @@ -28,4 +29,10 @@ class ClassAttributeAccessorTest < Test::Unit::TestCase assert @object.respond_to?(:bar) assert !@object.respond_to?(:bar=) end + + + def test_should_not_create_instance_reader + assert @class.respond_to?(:shaq) + assert !@object.respond_to?(:shaq) + end end diff --git a/activesupport/test/core_ext/module/attribute_accessor_test.rb b/activesupport/test/core_ext/module/attribute_accessor_test.rb index 9697508..af64a07 100644 --- a/activesupport/test/core_ext/module/attribute_accessor_test.rb +++ b/activesupport/test/core_ext/module/attribute_accessor_test.rb @@ -4,7 +4,8 @@ class ModuleAttributeAccessorTest < Test::Unit::TestCase def setup m = @module = Module.new do mattr_accessor :foo - mattr_accessor :bar, :instance_writer => false + mattr_accessor :bar, :instance_writer => false + mattr_reader :shaq, :instance_reader => false end @class = Class.new @class.instance_eval { include m } @@ -30,4 +31,9 @@ class ModuleAttributeAccessorTest < Test::Unit::TestCase assert @object.respond_to?(:bar) assert !@object.respond_to?(:bar=) end + + def test_should_not_create_instance_reader + assert @module.respond_to?(:shaq) + assert !@object.respond_to?(:shaq) + end end -- 1.6.4.2 From 03d5d0b5f50161d8ec1b2b627e483aad7100494a Mon Sep 17 00:00:00 2001 From: Andrew White Date: Mon, 29 Mar 2010 09:46:04 +0100 Subject: [PATCH 549/779] Add the ability to specify table_name_prefix on individual modules [#4032 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/CHANGELOG | 2 + activerecord/lib/active_record/base.rb | 9 +++++++- activerecord/test/cases/modules_test.rb | 28 +++++++++++++++++++++++++ activerecord/test/models/company_in_module.rb | 17 +++++++++++++++ 4 files changed, 55 insertions(+), 1 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 9b38bf4..e99265e 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *2.3.6 (pending)* +* To prefix the table names of all models in a module, define self.table_name_prefix on the module. #4032 [Andrew White] + * Association inverses for belongs_to, has_one, and has_many. Optimization to reduce database queries. #3533 [Murray Steele] # post.comments sets each comment's post without needing to :include diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index eed35e2..3aab5c0 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -461,6 +461,9 @@ module ActiveRecord #:nodoc: # Accessor for the name of the prefix string to prepend to every table name. So if set to "basecamp_", all # table names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient way of creating a namespace # for tables in a shared database. By default, the prefix is the empty string. + # + # If you are organising your models within modules you can add a prefix to the models within a namespace by defining + # a singleton method in the parent module called table_name_prefix which returns your chosen prefix. cattr_accessor :table_name_prefix, :instance_writer => false @@table_name_prefix = "" @@ -1171,7 +1174,7 @@ module ActiveRecord #:nodoc: contained = contained.singularize if parent.pluralize_table_names contained << '_' end - name = "#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}" + name = "#{full_table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}" end set_table_name(name) @@ -1201,6 +1204,10 @@ module ActiveRecord #:nodoc: key end + def full_table_name_prefix #:nodoc: + (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix + end + # Defines the column name for use with single table inheritance # -- can be set in subclasses like so: self.inheritance_column = "type_id" def inheritance_column diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb index 4f559bc..62d5401 100644 --- a/activerecord/test/cases/modules_test.rb +++ b/activerecord/test/cases/modules_test.rb @@ -78,4 +78,32 @@ class ModulesTest < ActiveRecord::TestCase end end end + + def test_module_table_name_prefix + assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_prefix' + assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_prefix' + assert_equal 'companies', MyApplication::Business::Prefixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed' + end + + def test_module_table_name_prefix_with_global_prefix + classes = [ MyApplication::Business::Company, + MyApplication::Business::Firm, + MyApplication::Business::Client, + MyApplication::Business::Client::Contact, + MyApplication::Business::Developer, + MyApplication::Business::Project, + MyApplication::Business::Prefixed::Company, + MyApplication::Business::Prefixed::Nested::Company, + MyApplication::Billing::Account ] + + ActiveRecord::Base.table_name_prefix = 'global_' + classes.each(&:reset_table_name) + assert_equal 'global_companies', MyApplication::Business::Company.table_name, 'inferred table_name for ActiveRecord model in module without table_name_prefix' + assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_prefix' + assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_prefix' + assert_equal 'companies', MyApplication::Business::Prefixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed' + ensure + ActiveRecord::Base.table_name_prefix = '' + classes.each(&:reset_table_name) + end end diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb index e071525..8cee583 100644 --- a/activerecord/test/models/company_in_module.rb +++ b/activerecord/test/models/company_in_module.rb @@ -30,6 +30,23 @@ module MyApplication has_and_belongs_to_many :developers end + module Prefixed + def self.table_name_prefix + 'prefixed_' + end + + class Company < ActiveRecord::Base + end + + class Firm < Company + self.table_name = 'companies' + end + + module Nested + class Company < ActiveRecord::Base + end + end + end end module Billing -- 1.6.4.2 From 00b95eb265ff606ae0a7b650265aa3c6fd43a1da Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Thu, 11 Feb 2010 12:30:33 -0500 Subject: [PATCH 550/779] belongs_to associations now honor :primary_key option for joins Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/associations.rb | 2 +- .../associations/belongs_to_associations_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 3ff8aba..eeaccd9 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2173,7 +2173,7 @@ module ActiveRecord " #{join_type} %s ON %s.%s = %s.%s " % [ table_name_and_alias, connection.quote_table_name(aliased_table_name), - reflection.klass.primary_key, + reflection.options[:primary_key] || reflection.klass.primary_key, connection.quote_table_name(parent.aliased_table_name), options[:foreign_key] || reflection.primary_key_name ] diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 9f3945f..cccc5cd 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -31,6 +31,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal companies(:first_firm).name, client.firm_with_primary_key.name end + def test_belongs_to_with_primary_key_joins_on_correct_column + sql = Client.send(:construct_finder_sql, :joins => :firm_with_primary_key) + assert sql !~ /\.id/ + assert sql =~ /\.name/ + end + def test_proxy_assignment account = Account.find(1) assert_nothing_raised { account.firm = account.firm } -- 1.6.4.2 From 715b34fdff1e1e93204e57737d69f8d365e17504 Mon Sep 17 00:00:00 2001 From: Joey Aghion Date: Thu, 27 Aug 2009 16:06:44 -0400 Subject: [PATCH 551/779] use supplied primary key when eager-loading belongs_to associations rather than default primary key [#765] Signed-off-by: Jeremy Kemper --- .../lib/active_record/association_preload.rb | 2 +- .../associations/belongs_to_associations_test.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 967fff4..3f769e4 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -323,7 +323,7 @@ module ActiveRecord klass = klass_name.constantize table_name = klass.quoted_table_name - primary_key = klass.primary_key + primary_key = reflection.options[:primary_key] || klass.primary_key column_type = klass.columns.detect{|c| c.name == primary_key}.type ids = id_map.keys.map do |id| if column_type == :integer diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index cccc5cd..ab0edee 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -66,6 +66,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal apple.name, citibank.firm_name end + def test_eager_loading_with_primary_key + apple = Firm.create("name" => "Apple") + citibank = Client.create("name" => "Citibank", :firm_name => "Apple") + citibank_result = Client.find(:first, :conditions => {:name => "Citibank"}, :include => :firm_with_primary_key) + assert_not_nil citibank_result.instance_variable_get("@firm_with_primary_key") + end + def test_no_unexpected_aliasing first_firm = companies(:first_firm) another_firm = companies(:another_firm) -- 1.6.4.2 From bf563bd90422a77fb07f2b7261d60c956c485fe8 Mon Sep 17 00:00:00 2001 From: Dudley Flanders Date: Mon, 29 Mar 2010 16:22:28 -0500 Subject: [PATCH 552/779] Trivial doc update on nested attributes delete renaming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_view/helpers/form_helper.rb | 8 ++++---- railties/guides/source/getting_started.textile | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index e775f1d..c5b1a03 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -391,7 +391,7 @@ module ActionView # accepts_nested_attributes_for :address, :allow_destroy => true # end # - # Now, when you use a form element with the _delete parameter, + # Now, when you use a form element with the _destroy parameter, # with a value that evaluates to +true+, you will destroy the associated # model (eg. 1, '1', true, or 'true'): # @@ -399,7 +399,7 @@ module ActionView # ... # <% person_form.fields_for :address do |address_fields| %> # ... - # Delete: <%= address_fields.check_box :_delete %> + # Delete: <%= address_fields.check_box :_destroy %> # <% end %> # <% end %> # @@ -472,14 +472,14 @@ module ActionView # end # # This will allow you to specify which models to destroy in the - # attributes hash by adding a form element for the _delete + # attributes hash by adding a form element for the _destroy # parameter with a value that evaluates to +true+ # (eg. 1, '1', true, or 'true'): # # <% form_for @person, :url => { :action => "update" } do |person_form| %> # ... # <% person_form.fields_for :projects do |project_fields| %> - # Delete: <%= project_fields.check_box :_delete %> + # Delete: <%= project_fields.check_box :_destroy %> # <% end %> # <% end %> def fields_for(record_or_name_or_array, *args, &block) diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 97f141b..6a19b04 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -1255,8 +1255,8 @@ You'll also need to modify +views/posts/_form.html.erb+ to include the tags:

<% unless tag_form.object.nil? || tag_form.object.new_record? %>

- <%= tag_form.label :_delete, 'Remove:' %> - <%= tag_form.check_box :_delete %> + <%= tag_form.label :_destroy, 'Remove:' %> + <%= tag_form.check_box :_destroy %>

<% end %> <% end %> -- 1.6.4.2 From ea6ef768a7718f9b4733c7db5b0a49d57440a2d9 Mon Sep 17 00:00:00 2001 From: Joseph Wilk Date: Thu, 12 Mar 2009 13:21:22 +0000 Subject: [PATCH 553/779] Fixed a bug where create_table could not be called without a block [#2221 state:resolved] Signed-off-by: Pratik Naik --- .../abstract/schema_statements.rb | 2 +- activerecord/test/cases/migration_test.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 7bbef88..800c601 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -101,7 +101,7 @@ module ActiveRecord table_definition = TableDefinition.new(self) table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false - yield table_definition + yield table_definition if block_given? if options[:force] && table_exists?(table_name) drop_table(table_name, options) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 1ceffd7..3d71e50 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -311,6 +311,13 @@ if ActiveRecord::Base.connection.supports_migrations? Person.connection.drop_table table_name rescue nil end + def test_create_table_without_a_block + table_name = :testings + Person.connection.create_table table_name + ensure + Person.connection.drop_table table_name rescue nil + end + # Sybase, and SQLite3 will not allow you to add a NOT NULL # column to a table without a default value. unless current_adapter?(:SybaseAdapter, :SQLiteAdapter) -- 1.6.4.2 From ccb1beeb5bf722d6951fa0b5bf2b9932af3593f8 Mon Sep 17 00:00:00 2001 From: Juanjo Bazan Date: Tue, 30 Mar 2010 23:18:44 +0200 Subject: [PATCH 554/779] new assertion: assert_blank Signed-off-by: Xavier Noria --- .../lib/active_support/testing/assertions.rb | 7 +++++ activesupport/test/test_test.rb | 28 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index ca51adb..e303d1f 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -60,6 +60,13 @@ module ActiveSupport def assert_no_difference(expression, message = nil, &block) assert_difference expression, 0, message, &block end + + # Test if an expression is blank. Passes if object.blank? is true. + # + # assert_blank [] # => true + def assert_blank(object) + assert object.blank?, "#{object.inspect} is not blank" + end end end end diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb index d250b10..daf273a 100644 --- a/activesupport/test/test_test.rb +++ b/activesupport/test/test_test.rb @@ -85,6 +85,34 @@ class AssertDifferenceTest < ActiveSupport::TestCase end end +class EmptyTrue + def empty?() true; end +end + +class EmptyFalse + def empty?() false; end +end + +class AssertBlankTest < ActiveSupport::TestCase + BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ] + NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'j', [nil], { nil => 0 } ] + + def test_assert_blank_true + BLANK.each { |v| assert_blank v } + end + + def test_assert_blank_false + NOT_BLANK.each { |v| + begin + assert_blank v + fail 'should not get to here' + rescue Exception => e + assert_match(/is not blank/, e.message) + end + } + end +end + # These should always pass if ActiveSupport::Testing.const_defined?(:Default) class NotTestingThingsTest < Test::Unit::TestCase -- 1.6.4.2 From 4b08679ba9627884d531cf59a9bb2fd1d2c86d62 Mon Sep 17 00:00:00 2001 From: Juanjo Bazan Date: Tue, 30 Mar 2010 23:39:00 +0200 Subject: [PATCH 555/779] New assertion: assert_present Signed-off-by: Xavier Noria --- activesupport/CHANGELOG | 2 + .../lib/active_support/testing/assertions.rb | 7 ++++++ activesupport/test/test_test.rb | 22 +++++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletions(-) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 440c6e7..4ea0916 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *2.3.6 (pending)* +* New assertions assert_blank and assert_present. #4299 [Juanjo Bazan] + * Use Object#singleton_class instead of #metaclass. Prefer Ruby's choice. [Jeremy Kemper] * JSON backend for YAJL. Preferred if available. #2666 [Brian Lopez] diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index e303d1f..aa9dbe8 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -67,6 +67,13 @@ module ActiveSupport def assert_blank(object) assert object.blank?, "#{object.inspect} is not blank" end + + # Test if an expression is not blank. Passes if object.present? is true. + # + # assert_present {:data => 'x' } # => true + def assert_present(object) + assert object.present?, "#{object.inspect} is blank" + end end end end diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb index daf273a..fe4aa2e 100644 --- a/activesupport/test/test_test.rb +++ b/activesupport/test/test_test.rb @@ -95,7 +95,7 @@ end class AssertBlankTest < ActiveSupport::TestCase BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ] - NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'j', [nil], { nil => 0 } ] + NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'x', [nil], { nil => 0 } ] def test_assert_blank_true BLANK.each { |v| assert_blank v } @@ -113,6 +113,26 @@ class AssertBlankTest < ActiveSupport::TestCase end end +class AssertPresentTest < ActiveSupport::TestCase + BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ] + NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'x', [nil], { nil => 0 } ] + + def test_assert_blank_true + NOT_BLANK.each { |v| assert_present v } + end + + def test_assert_blank_false + BLANK.each { |v| + begin + assert_present v + fail 'should not get to here' + rescue Exception => e + assert_match(/is blank/, e.message) + end + } + end +end + # These should always pass if ActiveSupport::Testing.const_defined?(:Default) class NotTestingThingsTest < Test::Unit::TestCase -- 1.6.4.2 From e329eab0c9deec5180ccc8a0c7990239aa70b43e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 30 Mar 2010 20:20:25 -0300 Subject: [PATCH 556/779] Don't cache the utc_offset we are already caching the timezone [#4301] Signed-off-by: Jeremy Kemper --- .../lib/active_support/values/time_zone.rb | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 836f469..658c713 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -181,10 +181,12 @@ module ActiveSupport @name = name @utc_offset = utc_offset @tzinfo = tzinfo + @current_period = nil end def utc_offset - @utc_offset ||= tzinfo.current_period.utc_offset + @current_period ||= tzinfo.current_period + @current_period.utc_offset end # Returns the offset of this time zone as a formatted string, of the -- 1.6.4.2 From a0454dcd1a8771b78e836a8e8f87a8a2fca320f8 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 30 Mar 2010 17:23:44 -0700 Subject: [PATCH 557/779] avoid method redefined; discarding old empty? warning [Santiago Pastorino] --- activesupport/test/abstract_unit.rb | 1 + activesupport/test/core_ext/blank_test.rb | 8 -------- activesupport/test/empty_bool.rb | 8 ++++++++ activesupport/test/test_test.rb | 8 -------- 4 files changed, 9 insertions(+), 16 deletions(-) create mode 100644 activesupport/test/empty_bool.rb diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index 9ded5f8..c2ed015 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -1,5 +1,6 @@ require 'rubygems' require 'test/unit' +require 'empty_bool' ENV['NO_RELOAD'] = '1' diff --git a/activesupport/test/core_ext/blank_test.rb b/activesupport/test/core_ext/blank_test.rb index 00fea74..3997040 100644 --- a/activesupport/test/core_ext/blank_test.rb +++ b/activesupport/test/core_ext/blank_test.rb @@ -1,13 +1,5 @@ require 'abstract_unit' -class EmptyTrue - def empty?() true; end -end - -class EmptyFalse - def empty?() false; end -end - class BlankTest < Test::Unit::TestCase BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ] NOT = [ EmptyFalse.new, Object.new, true, 0, 1, 'a', [nil], { nil => 0 } ] diff --git a/activesupport/test/empty_bool.rb b/activesupport/test/empty_bool.rb new file mode 100644 index 0000000..ad71a9b --- /dev/null +++ b/activesupport/test/empty_bool.rb @@ -0,0 +1,8 @@ +class EmptyTrue + def empty?() true; end +end + +class EmptyFalse + def empty?() false; end +end + diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb index fe4aa2e..5851980 100644 --- a/activesupport/test/test_test.rb +++ b/activesupport/test/test_test.rb @@ -85,14 +85,6 @@ class AssertDifferenceTest < ActiveSupport::TestCase end end -class EmptyTrue - def empty?() true; end -end - -class EmptyFalse - def empty?() false; end -end - class AssertBlankTest < ActiveSupport::TestCase BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ] NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'x', [nil], { nil => 0 } ] -- 1.6.4.2 From 0e57c70baf11a7f0211f78a5282f8ba26fbc17b4 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 31 Mar 2010 12:55:49 +0100 Subject: [PATCH 558/779] Dont try to load the record from the db if preloading didn't find anything --- .../lib/active_record/association_preload.rb | 5 +++++ activerecord/test/cases/associations/eager_test.rb | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 3f769e4..2310649 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -157,6 +157,11 @@ module ActiveRecord association_proxy.__send__(:set_inverse_instance, associated_record, mapped_record) end end + + id_to_record_map.each do |id, records| + next if seen_keys.include?(id.to_s) + records.each {|record| record.send("set_#{reflection_name}_target", nil) } + end end # Given a collection of ActiveRecord objects, constructs a Hash which maps diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index d23f86b..2bc5e36 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -830,5 +830,11 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_equal expected, firm.account_using_primary_key end end - + + def test_preloading_empty_polymorphic_parent + t = Tagging.create!(:taggable_type => 'Post', :taggable_id => Post.maximum(:id) + 1, :tag => tags(:general)) + + assert_queries(2) { @tagging = Tagging.preload(:taggable).find(t.id) } + assert_no_queries { assert ! @tagging.taggable } + end end -- 1.6.4.2 From 1f44fc90c68132a7ab3a86526cf867d13c5291af Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 31 Mar 2010 14:14:56 +0100 Subject: [PATCH 559/779] Dont use Rails 3 finder syntax in Rails 2.3.x test [#4303 state:resolved] --- activerecord/test/cases/associations/eager_test.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 2bc5e36..9d3be8d 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -834,7 +834,7 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_preloading_empty_polymorphic_parent t = Tagging.create!(:taggable_type => 'Post', :taggable_id => Post.maximum(:id) + 1, :tag => tags(:general)) - assert_queries(2) { @tagging = Tagging.preload(:taggable).find(t.id) } + assert_queries(2) { @tagging = Tagging.find(t.id, :include => :taggable) } assert_no_queries { assert ! @tagging.taggable } end end -- 1.6.4.2 From 1668ad3baf36aa1fd495e7229c46ebbdebdffd27 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 27 Dec 2009 17:54:43 -0800 Subject: [PATCH 560/779] Added Object#presence that returns the object if it's #present? otherwise returns nil [DHH/Colin Kelley] --- activesupport/CHANGELOG | 2 + .../lib/active_support/core_ext/object/blank.rb | 22 ++++++++++++++++++- activesupport/test/core_ext/blank_test.rb | 9 ++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 4ea0916..39d7b25 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *2.3.6 (pending)* +* Added Object#presence that returns the object if it's #present? otherwise returns nil [DHH/Colin Kelley] + * New assertions assert_blank and assert_present. #4299 [Juanjo Bazan] * Use Object#singleton_class instead of #metaclass. Prefer Ruby's choice. [Jeremy Kemper] diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb index 9a1f663..eb99bb1 100644 --- a/activesupport/lib/active_support/core_ext/object/blank.rb +++ b/activesupport/lib/active_support/core_ext/object/blank.rb @@ -2,11 +2,11 @@ class Object # An object is blank if it's false, empty, or a whitespace string. # For example, "", " ", +nil+, [], and {} are blank. # - # This simplifies + # This simplifies: # # if !address.nil? && !address.empty? # - # to + # ...to: # # if !address.blank? def blank? @@ -17,6 +17,24 @@ class Object def present? !blank? end + + # Returns object if it's #present? otherwise returns nil. + # object.presence is equivalent to object.present? ? object : nil. + # + # This is handy for any representation of objects where blank is the same + # as not present at all. For example, this simplifies a common check for + # HTTP POST/query parameters: + # + # state = params[:state] if params[:state].present? + # country = params[:country] if params[:country].present? + # region = state || country || 'US' + # + # ...becomes: + # + # region = params[:state].presence || params[:country].presence || 'US' + def presence + self if present? + end end class NilClass #:nodoc: diff --git a/activesupport/test/core_ext/blank_test.rb b/activesupport/test/core_ext/blank_test.rb index 3997040..4ed0137 100644 --- a/activesupport/test/core_ext/blank_test.rb +++ b/activesupport/test/core_ext/blank_test.rb @@ -5,12 +5,17 @@ class BlankTest < Test::Unit::TestCase NOT = [ EmptyFalse.new, Object.new, true, 0, 1, 'a', [nil], { nil => 0 } ] def test_blank - BLANK.each { |v| assert v.blank?, "#{v.inspect} should be blank" } + BLANK.each { |v| assert v.blank?, "#{v.inspect} should be blank" } NOT.each { |v| assert !v.blank?, "#{v.inspect} should not be blank" } end def test_present BLANK.each { |v| assert !v.present?, "#{v.inspect} should not be present" } - NOT.each { |v| assert v.present?, "#{v.inspect} should be present" } + NOT.each { |v| assert v.present?, "#{v.inspect} should be present" } + end + + def test_presence + BLANK.each { |v| assert_equal nil, v.presence, "#{v.inspect}.presence should return nil" } + NOT.each { |v| assert_equal v, v.presence, "#{v.inspect}.presence should return self" } end end -- 1.6.4.2 From aa48c79ae41e34825b79eb753d61bb8157271a94 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 31 Mar 2010 19:49:29 -0700 Subject: [PATCH 561/779] HTML safety: give a deprecation warning if an array of option tags is passed to select tag. Be sure to join the tag yourself and mark them .html_safe --- .../lib/action_view/helpers/form_tag_helper.rb | 3 +++ actionpack/test/template/form_tag_helper_test.rb | 6 ++++++ 2 files changed, 9 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index c0382da..4b589c2 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -78,6 +78,9 @@ module ActionView # # def select_tag(name, option_tags = nil, options = {}) html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name + if Array === option_tags + ActiveSupport::Deprecation.warn 'Passing an array of option_tags to select_tag implicitly joins them without marking them as HTML-safe. Pass option_tags.join.html_safe instead.', caller + end content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 2559b7a..4606047 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -135,6 +135,12 @@ class FormTagHelperTest < ActionView::TestCase assert_match VALID_HTML_ID, input_elem['id'] end + def test_select_tag_with_array_options + assert_deprecated /array/ do + select_tag "people", [""] + end + end + def test_text_area_tag_size_string actual = text_area_tag "body", "hello world", "size" => "20x40" expected = %() -- 1.6.4.2 From de7925de8a80b5387be27a41abac185b351a746a Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 2 Apr 2010 16:53:45 -0300 Subject: [PATCH 562/779] utc_offset is no longer required on TimeZone and if it's not supplied we delegate to TZInfo --- .../lib/active_support/values/time_zone.rb | 18 +++++++++++++----- activesupport/test/time_zone_test.rb | 5 ++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 658c713..eafdb93 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -177,7 +177,7 @@ module ActiveSupport # offset is the number of seconds that this time zone is offset from UTC # (GMT). Seconds were chosen as the offset unit because that is the unit that # Ruby uses to represent time zone offsets (see Time#utc_offset). - def initialize(name, utc_offset, tzinfo = nil) + def initialize(name, utc_offset = nil, tzinfo = nil) @name = name @utc_offset = utc_offset @tzinfo = tzinfo @@ -185,8 +185,12 @@ module ActiveSupport end def utc_offset - @current_period ||= tzinfo.current_period - @current_period.utc_offset + if @utc_offset + @utc_offset + else + @current_period ||= tzinfo.current_period + @current_period.utc_offset + end end # Returns the offset of this time zone as a formatted string, of the @@ -288,10 +292,14 @@ module ActiveSupport tzinfo.period_for_local(time, dst) end - # TODO: Preload instead of lazy load for thread safety def tzinfo + @tzinfo ||= find_tzinfo + end + + # TODO: Preload instead of lazy load for thread safety + def find_tzinfo require 'tzinfo' unless defined?(TZInfo) - @tzinfo ||= TZInfo::Timezone.get(MAPPING[name]) + ::TZInfo::Timezone.get(MAPPING[name]) end unless const_defined?(:ZONES) diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index b01f624..7e5387e 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -39,9 +39,8 @@ class TimeZoneTest < Test::Unit::TestCase assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone[zone.name] end - define_method("test_utc_offset_for_#{name}") do - period = zone.tzinfo.current_period - assert_equal period.utc_offset, zone.utc_offset + define_method("test_zones_map_for_#{name}") do + assert_equal ActiveSupport::TimeZone::ZONES_MAP[zone.name], zone end end -- 1.6.4.2 From 2c148cd96aeb9f0bd22f2c130b8b009ebd37e2b7 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 2 Apr 2010 16:53:45 -0300 Subject: [PATCH 563/779] delegate unknown timezones to TZInfo --- .../lib/active_support/values/time_zone.rb | 17 ++++++++++++----- activesupport/test/time_zone_test.rb | 8 ++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index eafdb93..73a27ac 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -293,13 +293,15 @@ module ActiveSupport end def tzinfo - @tzinfo ||= find_tzinfo + @tzinfo ||= TimeZone.find_tzinfo(name) end # TODO: Preload instead of lazy load for thread safety - def find_tzinfo + def self.find_tzinfo(name) require 'tzinfo' unless defined?(TZInfo) - ::TZInfo::Timezone.get(MAPPING[name]) + ::TZInfo::Timezone.get(MAPPING[name] || name) + rescue TZInfo::InvalidTimezoneIdentifier + nil end unless const_defined?(:ZONES) @@ -364,7 +366,6 @@ module ActiveSupport end ZONES.sort! ZONES.freeze - ZONES_MAP.freeze US_ZONES = ZONES.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ } US_ZONES.freeze @@ -395,7 +396,7 @@ module ActiveSupport def [](arg) case arg when String - ZONES_MAP[arg] + ZONES_MAP[arg] ||= lookup(arg) when Numeric, ActiveSupport::Duration arg *= 3600 if arg.abs <= 13 all.find { |z| z.utc_offset == arg.to_i } @@ -409,6 +410,12 @@ module ActiveSupport def us_zones US_ZONES end + + private + + def lookup(name) + (tzinfo = find_tzinfo(name)) && create(tzinfo.name.freeze) + end end end end diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 7e5387e..c6b47f6 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -73,6 +73,14 @@ class TimeZoneTest < Test::Unit::TestCase end end + def test_unknown_timezones_delegation_to_tzinfo + zone = ActiveSupport::TimeZone['America/Montevideo'] + assert_equal ActiveSupport::TimeZone, zone.class + assert_equal zone.object_id, ActiveSupport::TimeZone['America/Montevideo'].object_id + assert_equal Time.utc(2010, 1, 31, 22), zone.utc_to_local(Time.utc(2010, 2)) # daylight saving offset -0200 + assert_equal Time.utc(2010, 3, 31, 21), zone.utc_to_local(Time.utc(2010, 4)) # standard offset -0300 + end + def test_today Time.stubs(:now).returns(Time.utc(2000, 1, 1, 4, 59, 59)) # 1 sec before midnight Jan 1 EST assert_equal Date.new(1999, 12, 31), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].today -- 1.6.4.2 From e2ec41a9a6f0e708eefd84fb026730ffa671fdcb Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 2 Apr 2010 14:01:26 -0700 Subject: [PATCH 564/779] CI: add tzinfo --- ci/geminstaller.yml | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml index efcb413..4f0c6a2 100644 --- a/ci/geminstaller.yml +++ b/ci/geminstaller.yml @@ -22,3 +22,5 @@ gems: version: >= 2.2.3 - name: sqlite3-ruby version: >= 1.2.5 +- name: tzinfo + version: >= 0.3.18 -- 1.6.4.2 From 19161e08b33947d617d3562986983bd9b43abb28 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 2 Apr 2010 14:03:49 -0700 Subject: [PATCH 565/779] Adjust test in case tzinfo is not available --- activesupport/test/time_zone_test.rb | 12 ++++++++---- 1 files changed, 8 insertions(+), 4 deletions(-) diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index c6b47f6..b2bdea2 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -75,10 +75,14 @@ class TimeZoneTest < Test::Unit::TestCase def test_unknown_timezones_delegation_to_tzinfo zone = ActiveSupport::TimeZone['America/Montevideo'] - assert_equal ActiveSupport::TimeZone, zone.class - assert_equal zone.object_id, ActiveSupport::TimeZone['America/Montevideo'].object_id - assert_equal Time.utc(2010, 1, 31, 22), zone.utc_to_local(Time.utc(2010, 2)) # daylight saving offset -0200 - assert_equal Time.utc(2010, 3, 31, 21), zone.utc_to_local(Time.utc(2010, 4)) # standard offset -0300 + if defined?(TZInfo::TimeZone) + assert_equal ActiveSupport::TimeZone, zone.class + assert_equal zone.object_id, ActiveSupport::TimeZone['America/Montevideo'].object_id + assert_equal Time.utc(2010, 1, 31, 22), zone.utc_to_local(Time.utc(2010, 2)) # daylight saving offset -0200 + assert_equal Time.utc(2010, 3, 31, 21), zone.utc_to_local(Time.utc(2010, 4)) # standard offset -0300 + else + assert_nil zone + end end def test_today -- 1.6.4.2 From a72bcdb8aedd8cbfa0efc2ca95fe4c7553227f80 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 2 Apr 2010 14:39:03 -0700 Subject: [PATCH 566/779] Check more carefully for vendored tzinfo --- activesupport/test/time_zone_test.rb | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index b2bdea2..2802ffd 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -75,13 +75,16 @@ class TimeZoneTest < Test::Unit::TestCase def test_unknown_timezones_delegation_to_tzinfo zone = ActiveSupport::TimeZone['America/Montevideo'] - if defined?(TZInfo::TimeZone) + begin + require 'tzinfo/country' + rescue LoadError + # using vendored tzinfo which doesn't have tzinfo/country + assert_nil zone + else assert_equal ActiveSupport::TimeZone, zone.class assert_equal zone.object_id, ActiveSupport::TimeZone['America/Montevideo'].object_id assert_equal Time.utc(2010, 1, 31, 22), zone.utc_to_local(Time.utc(2010, 2)) # daylight saving offset -0200 assert_equal Time.utc(2010, 3, 31, 21), zone.utc_to_local(Time.utc(2010, 4)) # standard offset -0300 - else - assert_nil zone end end -- 1.6.4.2 From e617af13a2cee3278c6f76121bbb777fc7f0fea5 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sun, 4 Apr 2010 01:26:11 +0100 Subject: [PATCH 567/779] Backport of lazy evaluation of has_many ..., :dependent => :___ [#2627 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/associations.rb | 40 +++++++------------ activerecord/lib/active_record/reflection.rb | 11 +++++ .../associations/has_many_associations_test.rb | 18 +++++++++ 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index eeaccd9..53d28c0 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1415,14 +1415,6 @@ module ActiveRecord # finder conditions. def configure_dependency_for_has_many(reflection, extra_conditions = nil) if reflection.options.include?(:dependent) - # Add polymorphic type if the :as option is present - dependent_conditions = [] - dependent_conditions << "#{reflection.primary_key_name} = \#{record.#{reflection.name}.send(:owner_quoted_id)}" - dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as] - dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.quoted_table_name) if reflection.options[:conditions] - dependent_conditions << extra_conditions if extra_conditions - dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ") - dependent_conditions = dependent_conditions.gsub('@', '\@') case reflection.options[:dependent] when :destroy method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym @@ -1431,24 +1423,22 @@ module ActiveRecord end before_destroy method_name when :delete_all - module_eval %Q{ - before_destroy do |record| # before_destroy do |record| - delete_all_has_many_dependencies(record, # delete_all_has_many_dependencies(record, - "#{reflection.name}", # "posts", - #{reflection.class_name}, # Post, - %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...) - end # end - } + before_destroy do |record| + record.class.send(:delete_all_has_many_dependencies, + record, + reflection.name, + reflection.klass, + reflection.dependent_conditions(record, record.class, extra_conditions)) + end when :nullify - module_eval %Q{ - before_destroy do |record| # before_destroy do |record| - nullify_has_many_dependencies(record, # nullify_has_many_dependencies(record, - "#{reflection.name}", # "posts", - #{reflection.class_name}, # Post, - "#{reflection.primary_key_name}", # "user_id", - %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...) - end # end - } + before_destroy do |record| + record.class.send(:nullify_has_many_dependencies, + record, + reflection.name, + reflection.klass, + reflection.primary_key_name, + reflection.dependent_conditions(record, record.class, extra_conditions)) + end else raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})" end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 4fcb8b3..13bcb8b 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -277,6 +277,17 @@ module ActiveRecord !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many) end + def dependent_conditions(record, base_class, extra_conditions) + dependent_conditions = [] + dependent_conditions << "#{primary_key_name} = #{record.send(name).send(:owner_quoted_id)}" + dependent_conditions << "#{options[:as]}_type = '#{base_class.name}'" if options[:as] + dependent_conditions << klass.send(:sanitize_sql, options[:conditions]) if options[:conditions] + dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ") + dependent_conditions << extra_conditions if extra_conditions + dependent_conditions = dependent_conditions.gsub('@', '\@') + dependent_conditions + end + private def derive_class_name class_name = name.to_s.camelize diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 2d1a42e..403eeef 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1138,5 +1138,23 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_instance_eval_in_association_proxy assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.sort_by(&:id).first.instance_eval{title} end + + def test_defining_has_many_association_with_delete_all_dependency_lazily_evaluates_target_class + ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never + class_eval <<-EOF + class DeleteAllModel < ActiveRecord::Base + has_many :nonentities, :dependent => :delete_all + end + EOF + end + + def test_defining_has_many_association_with_nullify_dependency_lazily_evaluates_target_class + ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never + class_eval <<-EOF + class NullifyModel < ActiveRecord::Base + has_many :nonentities, :dependent => :nullify + end + EOF + end end -- 1.6.4.2 From 642d5d297e9569a76047c958b8e4aa23522f300d Mon Sep 17 00:00:00 2001 From: Kristopher Murata Date: Sun, 4 Apr 2010 14:45:33 -0400 Subject: [PATCH 568/779] Parameterize should accept malformed utf8 characters [#4323 state:resolved] Signed-off-by: Jeremy Kemper --- activesupport/lib/active_support/inflector.rb | 3 +++ activesupport/test/inflector_test_cases.rb | 15 ++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index 71f4c44..5a3df58 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -1,6 +1,7 @@ # encoding: utf-8 require 'singleton' require 'iconv' +require 'kconv' module ActiveSupport # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without, @@ -257,6 +258,8 @@ module ActiveSupport # <%= link_to(@person.name, person_path(@person)) %> # # => Donald E. Knuth def parameterize(string, sep = '-') + # remove malformed utf8 characters + string = string.toutf8 unless string.is_utf8? # replace accented chars with ther ascii equivalents parameterized_string = transliterate(string) # Turn unwanted chars into the seperator diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb index dbd9059..6908291 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -152,7 +152,8 @@ module InflectorTestCases "Trailing bad characters!@#" => "trailing-bad-characters", "!@#Leading bad characters" => "leading-bad-characters", "Squeeze separators" => "squeeze-separators", - "Test with + sign" => "test-with-sign" + "Test with + sign" => "test-with-sign", + "Test with malformed utf8 \251" => "test-with-malformed-utf8" } StringToParameterizeWithNoSeparator = { @@ -161,7 +162,8 @@ module InflectorTestCases "Trailing bad characters!@#" => "trailingbadcharacters", "!@#Leading bad characters" => "leadingbadcharacters", "Squeeze separators" => "squeezeseparators", - "Test with + sign" => "testwithsign" + "Test with + sign" => "testwithsign", + "Test with malformed utf8 \251" => "testwithmalformedutf8" } StringToParameterizeWithUnderscore = { @@ -170,19 +172,22 @@ module InflectorTestCases "Trailing bad characters!@#" => "trailing_bad_characters", "!@#Leading bad characters" => "leading_bad_characters", "Squeeze separators" => "squeeze_separators", - "Test with + sign" => "test_with_sign" + "Test with + sign" => "test_with_sign", + "Test with malformed utf8 \251" => "test_with_malformed_utf8" } # Ruby 1.9 doesn't do Unicode normalization yet. if RUBY_VERSION >= '1.9' StringToParameterizedAndNormalized = { "Malmö" => "malm", - "Garçons" => "gar-ons" + "Garçons" => "gar-ons", + "Ops \251" => "ops" } else StringToParameterizedAndNormalized = { "Malmö" => "malmo", - "Garçons" => "garcons" + "Garçons" => "garcons", + "Ops \251" => "ops" } end -- 1.6.4.2 From 1d7368200f7ae39e2f058d0b8a31b63f914ed0a2 Mon Sep 17 00:00:00 2001 From: Simon Effenberg Date: Wed, 31 Mar 2010 01:24:44 +0200 Subject: [PATCH 569/779] remove_index now uses quote_table_name() [#4300 state:resolved] Signed-off-by: Michael Koziarski --- .../abstract/schema_statements.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 800c601..d93cbc1 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -283,7 +283,7 @@ module ActiveRecord # Remove the index named by_branch_party in the accounts table. # remove_index :accounts, :name => :by_branch_party def remove_index(table_name, options = {}) - execute "DROP INDEX #{quote_column_name(index_name(table_name, options))} ON #{table_name}" + execute "DROP INDEX #{quote_column_name(index_name(table_name, options))} ON #{quote_table_name(table_name)}" end def index_name(table_name, options) #:nodoc: -- 1.6.4.2 From cfb31edb5441f0b12693a3b509dadf87fe1df843 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 7 Apr 2010 01:24:26 -0300 Subject: [PATCH 570/779] Generate routes for nested resources with nil object raise RoutingError [#4262 state:committed] Signed-off-by: Jeremy Kemper --- .../lib/action_controller/routing/route_set.rb | 1 + actionpack/test/template/url_helper_test.rb | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb index 8e4ed7b..2fbf3e9 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -174,6 +174,7 @@ module ActionController # named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks def #{selector}(*args) # def users_url(*args) + args.compact! # # #{generate_optimisation_block(route, kind)} # #{generate_optimisation_block(route, kind)} # diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index cf89829..9d541fc 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -566,6 +566,10 @@ class PolymorphicControllerTest < ActionView::TestCase render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>" end + def show_workshop_of_nil_sessions + render :inline => "<%= workshop_sessions_path(nil) %>" + end + def rescue_action(e) raise e end end @@ -612,6 +616,16 @@ class PolymorphicControllerTest < ActionView::TestCase end end + def test_existing_nested_resource_with_nil_id + @controller = SessionsController.new + + with_restful_routing do + assert_raise ActionController::RoutingError do + get :show_workshop_of_nil_sessions + end + end + end + protected def with_restful_routing with_routing do |set| @@ -619,6 +633,7 @@ class PolymorphicControllerTest < ActionView::TestCase map.resources :workshops do |w| w.resources :sessions end + map.show_workshop_of_nil_sessions 'sessions/show_workshop_of_nil_sessions', :controller => 'sessions', :action => 'show_workshop_of_nil_sessions' end yield end -- 1.6.4.2 From 0653a6d30e1a2746c6fbab768a989e2b3750cb32 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 8 Apr 2010 17:15:11 -0700 Subject: [PATCH 571/779] Fixed that default locale templates should be used if the current locale template is missing [DHH] --- actionpack/CHANGELOG | 2 + actionpack/lib/action_view/paths.rb | 8 +++++++ .../test/controller/localized_templates_test.rb | 22 ++++++++++++++++++++ .../test/fixtures/localized/hello_world.de.html | 1 + .../test/fixtures/localized/hello_world.en.html | 1 + 5 files changed, 34 insertions(+), 0 deletions(-) create mode 100644 actionpack/test/controller/localized_templates_test.rb create mode 100644 actionpack/test/fixtures/localized/hello_world.de.html create mode 100644 actionpack/test/fixtures/localized/hello_world.en.html diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 69a954a..4852d70 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Fixed that default locale templates should be used if the current locale template is missing [DHH] + * Fixed that PrototypeHelper#update_page should return html_safe [DHH] * Fixed that much of DateHelper wouldn't return html_safe? strings [DHH] diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index a0a2f96..9c1a2bd 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -47,15 +47,23 @@ module ActionView #:nodoc: each do |load_path| if format && (template = load_path["#{template_path}.#{I18n.locale}.#{format}"]) return template + # Try the default locale version if the current locale doesn't have one + # (i.e. you haven't translated this view to German yet, but you have the English version on hand) + elsif format && (template = load_path["#{template_path}.#{I18n.default_locale}.#{format}"]) + return template elsif format && (template = load_path["#{template_path}.#{format}"]) return template elsif template = load_path["#{template_path}.#{I18n.locale}"] return template + elsif template = load_path["#{template_path}.#{I18n.default_locale}"] + return template elsif template = load_path[template_path] return template # Try to find html version if the format is javascript elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.locale}.html"] return template + elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.default_locale}.html"] + return template elsif format == :js && html_fallback && template = load_path["#{template_path}.html"] return template end diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb new file mode 100644 index 0000000..41ff2f3 --- /dev/null +++ b/actionpack/test/controller/localized_templates_test.rb @@ -0,0 +1,22 @@ +require 'abstract_unit' + +class LocalizedController < ActionController::Base + def hello_world + end +end + +class LocalizedTemplatesTest < ActionController::TestCase + tests LocalizedController + + def test_localized_template_is_used + I18n.locale = :de + get :hello_world + assert_equal "Gutten Tag", @response.body + end + + def test_default_locale_template_is_used_when_locale_is_missing + I18n.locale = :dk + get :hello_world + assert_equal "Hello World", @response.body + end +end \ No newline at end of file diff --git a/actionpack/test/fixtures/localized/hello_world.de.html b/actionpack/test/fixtures/localized/hello_world.de.html new file mode 100644 index 0000000..4727d7a --- /dev/null +++ b/actionpack/test/fixtures/localized/hello_world.de.html @@ -0,0 +1 @@ +Gutten Tag \ No newline at end of file diff --git a/actionpack/test/fixtures/localized/hello_world.en.html b/actionpack/test/fixtures/localized/hello_world.en.html new file mode 100644 index 0000000..5e1c309 --- /dev/null +++ b/actionpack/test/fixtures/localized/hello_world.en.html @@ -0,0 +1 @@ +Hello World \ No newline at end of file -- 1.6.4.2 From fcec7402eb6fb9af9b6a7115648119219120308e Mon Sep 17 00:00:00 2001 From: Rolf Bjaanes Date: Thu, 8 Apr 2010 21:55:51 +0200 Subject: [PATCH 572/779] Changed the way inflections for uncountables work for 'funky jeans' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/inflections.rb | 2 +- activesupport/lib/active_support/inflector.rb | 2 +- activesupport/test/inflector_test_cases.rb | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb index 8fb3fa9..e7b5387 100644 --- a/activesupport/lib/active_support/inflections.rb +++ b/activesupport/lib/active_support/inflections.rb @@ -51,6 +51,6 @@ module ActiveSupport inflect.irregular('move', 'moves') inflect.irregular('cow', 'kine') - inflect.uncountable(%w(equipment information rice money species series fish sheep)) + inflect.uncountable(%w(equipment information rice money species series fish sheep jeans)) end end diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index 5a3df58..f7c352b 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -158,7 +158,7 @@ module ActiveSupport def singularize(word) result = word.to_s.dup - if inflections.uncountables.include?(result.downcase) + if inflections.uncountables.any? { |inflection| result =~ /#{inflection}\Z/i } result else inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } diff --git a/activesupport/test/inflector_test_cases.rb b/activesupport/test/inflector_test_cases.rb index 6908291..ecaa359 100644 --- a/activesupport/test/inflector_test_cases.rb +++ b/activesupport/test/inflector_test_cases.rb @@ -12,7 +12,9 @@ module InflectorTestCases "stack" => "stacks", "wish" => "wishes", "fish" => "fish", - + "jeans" => "jeans", + "funky jeans" => "funky jeans", + "category" => "categories", "query" => "queries", "ability" => "abilities", -- 1.6.4.2 From f87a518f8130498f7d281e827fd7a4fe3c517537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 9 Apr 2010 09:23:09 +0200 Subject: [PATCH 573/779] Fix tests broken in 0653a6d30e1a2746c6fbab768a989e2b3750cb32 --- .../test/controller/localized_templates_test.rb | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb index 41ff2f3..e89e5a8 100644 --- a/actionpack/test/controller/localized_templates_test.rb +++ b/actionpack/test/controller/localized_templates_test.rb @@ -8,6 +8,8 @@ end class LocalizedTemplatesTest < ActionController::TestCase tests LocalizedController + teardown { I18n.locale = :en } + def test_localized_template_is_used I18n.locale = :de get :hello_world -- 1.6.4.2 From 958b0e977a263100e3767d749afc08986acc4e7e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 9 Apr 2010 16:22:04 -0300 Subject: [PATCH 574/779] fix stack trace lines on class_eval MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_controller/polymorphic_routes.rb | 2 +- actionpack/lib/action_view/helpers/form_helper.rb | 4 ++-- actionpack/test/template/form_helper_test.rb | 4 ++-- .../lib/active_record/attribute_methods.rb | 2 +- activerecord/lib/active_record/base.rb | 16 ++++++++-------- .../connection_adapters/abstract/query_cache.rb | 2 +- activerecord/lib/active_record/dirty.rb | 2 +- .../lib/active_record/nested_attributes.rb | 4 ++-- activeresource/lib/active_resource/base.rb | 4 ++-- activeresource/lib/active_resource/http_mock.rb | 4 ++-- .../lib/active_support/buffered_logger.rb | 2 +- .../active_support/cache/strategy/local_cache.rb | 2 +- .../lib/active_support/core_ext/module/aliasing.rb | 2 +- .../core_ext/module/attr_accessor_with_default.rb | 2 +- .../core_ext/module/synchronization.rb | 2 +- activesupport/lib/active_support/deprecation.rb | 2 +- activesupport/lib/active_support/memoizable.rb | 2 +- .../active_support/multibyte/unicode_database.rb | 4 ++-- .../lib/i18n/backend/interpolation_compiler.rb | 4 ++-- 19 files changed, 33 insertions(+), 33 deletions(-) diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index b30d441..612701d 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -115,7 +115,7 @@ module ActionController end %w(edit new).each do |action| - module_eval <<-EOT, __FILE__, __LINE__ + module_eval <<-EOT, __FILE__, __LINE__ + 1 def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {}) polymorphic_url( # polymorphic_url( record_or_hash, # record_or_hash, diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index c5b1a03..403211b 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -977,7 +977,7 @@ module ActionView end (field_helpers - %w(label check_box radio_button fields_for hidden_field)).each do |selector| - src = <<-end_src + src, line = <<-end_src, __FILE__ + 1 def #{selector}(method, options = {}) # def text_field(method, options = {}) @template.send( # @template.send( #{selector.inspect}, # "text_field", @@ -986,7 +986,7 @@ module ActionView objectify_options(options)) # objectify_options(options)) end # end end_src - class_eval src, __FILE__, __LINE__ + class_eval src, __FILE__, line end def fields_for(record_or_name_or_array, *args, &block) diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index e7f1b4f..d0c619d 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1140,12 +1140,12 @@ class FormHelperTest < ActionView::TestCase class LabelledFormBuilder < ActionView::Helpers::FormBuilder (field_helpers - %w(hidden_field)).each do |selector| - src = <<-END_SRC + src, line = <<-END_SRC, __LINE__ + 1 def #{selector}(field, *args, &proc) (" " + super + "
").html_safe end END_SRC - class_eval src, __FILE__, __LINE__ + class_eval src, __FILE__, line end end diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index e2d52aa..e71afa1 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -208,7 +208,7 @@ module ActiveRecord end begin - class_eval(method_definition, __FILE__, __LINE__) + class_eval(method_definition, __FILE__) rescue SyntaxError => err generated_methods.delete(attr_name) if logger diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3aab5c0..32d2854 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1891,7 +1891,7 @@ module ActiveRecord #:nodoc: # find(:first, options.merge(finder_options)) # end # end - self.class_eval %{ + self.class_eval <<-EOS, __FILE__, __LINE__ + 1 def self.#{method_id}(*args) options = args.extract_options! attributes = construct_attributes_from_arguments( @@ -1911,7 +1911,7 @@ module ActiveRecord #:nodoc: end #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang} end - }, __FILE__, __LINE__ + EOS send(method_id, *arguments) elsif match.instantiator? instantiator = match.instantiator @@ -1940,7 +1940,7 @@ module ActiveRecord #:nodoc: # record # end # end - self.class_eval %{ + self.class_eval <<-EOS, __FILE__, __LINE__ + 1 def self.#{method_id}(*args) guard_protected_attributes = false @@ -1966,14 +1966,14 @@ module ActiveRecord #:nodoc: record end end - }, __FILE__, __LINE__ + EOS send(method_id, *arguments, &block) end elsif match = DynamicScopeMatch.match(method_id) attribute_names = match.attribute_names super unless all_attributes_exists?(attribute_names) if match.scope? - self.class_eval %{ + self.class_eval <<-EOS, __FILE__, __LINE__ + 1 def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args) options = args.extract_options! # options = args.extract_options! attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments( @@ -1982,7 +1982,7 @@ module ActiveRecord #:nodoc: # scoped(:conditions => attributes) # scoped(:conditions => attributes) end # end - }, __FILE__, __LINE__ + EOS send(method_id, *arguments) end else @@ -2224,9 +2224,9 @@ module ActiveRecord #:nodoc: modularized_name = type_name_with_module(type_name) silence_warnings do begin - class_eval(modularized_name, __FILE__, __LINE__) + class_eval(modularized_name, __FILE__) rescue NameError - class_eval(type_name, __FILE__, __LINE__) + class_eval(type_name, __FILE__) end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 00c7109..166c17b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -13,7 +13,7 @@ module ActiveRecord def dirties_query_cache(base, *method_names) method_names.each do |method_name| - base.class_eval <<-end_code, __FILE__, __LINE__ + base.class_eval <<-end_code, __FILE__, __LINE__ + 1 def #{method_name}_with_query_dirty(*args) # def update_with_query_dirty(*args) clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled #{method_name}_without_query_dirty(*args) # update_without_query_dirty(*args) diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index ef0f3b8..0dd5793 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -173,7 +173,7 @@ module ActiveRecord def alias_attribute_with_dirty(new_name, old_name) alias_attribute_without_dirty(new_name, old_name) DIRTY_SUFFIXES.each do |suffix| - module_eval <<-STR, __FILE__, __LINE__+1 + module_eval <<-STR, __FILE__, __LINE__ + 1 def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end # def subject_changed?; self.title_changed?; end STR end diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index f81ece5..ae24b80 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -239,11 +239,11 @@ module ActiveRecord # def pirate_attributes=(attributes) # assign_nested_attributes_for_one_to_one_association(:pirate, attributes) # end - class_eval %{ + class_eval <<-EOS, __FILE__, __LINE__ + 1 def #{association_name}_attributes=(attributes) assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes) end - }, __FILE__, __LINE__ + EOS else raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?" end diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index e25002b..d904f40 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -431,11 +431,11 @@ module ActiveResource @prefix_parameters = nil # Redefine the new methods. - code = <<-end_code + code, line = <<-end_code, __LINE__ + 1 def prefix_source() "#{value}" end def prefix(options={}) "#{prefix_call}" end end_code - silence_warnings { instance_eval code, __FILE__, __LINE__ } + silence_warnings { instance_eval code, __FILE__, line } rescue logger.error "Couldn't set prefix: #{$!}\n #{code}" raise diff --git a/activeresource/lib/active_resource/http_mock.rb b/activeresource/lib/active_resource/http_mock.rb index 7d7e378..17ef4b4 100644 --- a/activeresource/lib/active_resource/http_mock.rb +++ b/activeresource/lib/active_resource/http_mock.rb @@ -57,7 +57,7 @@ module ActiveResource # def post(path, request_headers = {}, body = nil, status = 200, response_headers = {}) # @responses[Request.new(:post, path, nil, request_headers)] = Response.new(body || "", status, response_headers) # end - module_eval <<-EOE, __FILE__, __LINE__ + module_eval <<-EOE, __FILE__, __LINE__ + 1 def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {}) @responses << [Request.new(:#{method}, path, nil, request_headers), Response.new(body || "", status, response_headers)] end @@ -125,7 +125,7 @@ module ActiveResource # self.class.requests << request # self.class.responses.assoc(request).try(:second) || raise(InvalidRequestError.new("No response recorded for #{request}")) # end - module_eval <<-EOE, __FILE__, __LINE__ + module_eval <<-EOE, __FILE__, __LINE__ + 1 def #{method}(path, #{'body, ' if has_body}headers) request = ActiveResource::Request.new(:#{method}, path, #{has_body ? 'body, ' : 'nil, '}headers) self.class.requests << request diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index 33bcf32..6c35b19 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -67,7 +67,7 @@ module ActiveSupport end for severity in Severity.constants - class_eval <<-EOT, __FILE__, __LINE__ + class_eval <<-EOT, __FILE__, __LINE__ + 1 def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block) add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block) end # end diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index 91842ce..088ab04 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -15,7 +15,7 @@ module ActiveSupport def middleware @middleware ||= begin klass = Class.new - klass.class_eval(<<-EOS, __FILE__, __LINE__) + klass.class_eval(<<-EOS, __FILE__, __LINE__ + 1) def initialize(app) @app = app end diff --git a/activesupport/lib/active_support/core_ext/module/aliasing.rb b/activesupport/lib/active_support/core_ext/module/aliasing.rb index 10fa520..5dafd06 100644 --- a/activesupport/lib/active_support/core_ext/module/aliasing.rb +++ b/activesupport/lib/active_support/core_ext/module/aliasing.rb @@ -63,7 +63,7 @@ module ActiveSupport # e.subject = "Megastars" # e.title # => "Megastars" def alias_attribute(new_name, old_name) - module_eval <<-STR, __FILE__, __LINE__+1 + module_eval <<-STR, __FILE__, __LINE__ + 1 def #{new_name}; self.#{old_name}; end # def subject; self.title; end def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end diff --git a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb index 4d0198f..28ac89d 100644 --- a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb +++ b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb @@ -21,7 +21,7 @@ class Module def attr_accessor_with_default(sym, default = nil, &block) raise 'Default value or block required' unless !default.nil? || block define_method(sym, block_given? ? block : Proc.new { default }) - module_eval(<<-EVAL, __FILE__, __LINE__) + module_eval(<<-EVAL, __FILE__, __LINE__ + 1) def #{sym}=(value) # def age=(value) class << self; attr_reader :#{sym} end # class << self; attr_reader :age end @#{sym} = value # @age = value diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb index 069db3f..f645d8e 100644 --- a/activesupport/lib/active_support/core_ext/module/synchronization.rb +++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb @@ -25,7 +25,7 @@ class Module raise ArgumentError, "#{method} is already synchronized. Double synchronization is not currently supported." end - module_eval(<<-EOS, __FILE__, __LINE__) + module_eval(<<-EOS, __FILE__, __LINE__ + 1) def #{aliased_method}_with_synchronization#{punctuation}(*args, &block) # def expire_with_synchronization(*args, &block) #{with}.synchronize do # @@lock.synchronize do #{aliased_method}_without_synchronization#{punctuation}(*args, &block) # expire_without_synchronization(*args, &block) diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 36289cf..e334f89 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -90,7 +90,7 @@ module ActiveSupport method_names = method_names + options.keys method_names.each do |method_name| alias_method_chain(method_name, :deprecation) do |target, punctuation| - class_eval(<<-EOS, __FILE__, __LINE__) + class_eval(<<-EOS, __FILE__, __LINE__ + 1) def #{target}_with_deprecation#{punctuation}(*args, &block) # def generate_secret_with_deprecation(*args, &block) ::ActiveSupport::Deprecation.warn( # ::ActiveSupport::Deprecation.warn( self.class.deprecated_method_warning( # self.class.deprecated_method_warning( diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 9282bab..aa33ef7 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -58,7 +58,7 @@ module ActiveSupport original_method = :"_unmemoized_#{symbol}" memoized_ivar = ActiveSupport::Memoizable.memoized_ivar_for(symbol) - class_eval <<-EOS, __FILE__, __LINE__ + class_eval <<-EOS, __FILE__, __LINE__ + 1 include InstanceMethods # include InstanceMethods # if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type) diff --git a/activesupport/lib/active_support/multibyte/unicode_database.rb b/activesupport/lib/active_support/multibyte/unicode_database.rb index a08f38c..e9578b7 100644 --- a/activesupport/lib/active_support/multibyte/unicode_database.rb +++ b/activesupport/lib/active_support/multibyte/unicode_database.rb @@ -23,7 +23,7 @@ module ActiveSupport #:nodoc: # Lazy load the Unicode database so it's only loaded when it's actually used ATTRIBUTES.each do |attr_name| - class_eval(<<-EOS, __FILE__, __LINE__) + class_eval(<<-EOS, __FILE__, __LINE__ + 1) def #{attr_name} # def codepoints load # load @#{attr_name} # @codepoints @@ -68,4 +68,4 @@ module ActiveSupport #:nodoc: # UniCode Database UCD = UnicodeDatabase.new end -end \ No newline at end of file +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/interpolation_compiler.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/interpolation_compiler.rb index 6a14150..b3c9273 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/interpolation_compiler.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/interpolation_compiler.rb @@ -22,7 +22,7 @@ module I18n def compile_if_an_interpolation(string) if interpolated_str?(string) - string.instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + string.instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def i18n_interpolate(v = {}) "#{compiled_interpolation_body(string)}" end @@ -116,4 +116,4 @@ module I18n end end end -end \ No newline at end of file +end -- 1.6.4.2 From 4a02437a8df719e552b260d889281f075e46f3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 9 Apr 2010 22:19:49 +0200 Subject: [PATCH 575/779] Fix a failure added on 958b0e977a263100e3767d749afc08986acc4e7e --- actionpack/lib/action_view/helpers/form_helper.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 403211b..87e90e0 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -977,7 +977,7 @@ module ActionView end (field_helpers - %w(label check_box radio_button fields_for hidden_field)).each do |selector| - src, line = <<-end_src, __FILE__ + 1 + src, line = <<-end_src, __LINE__ + 1 def #{selector}(method, options = {}) # def text_field(method, options = {}) @template.send( # @template.send( #{selector.inspect}, # "text_field", -- 1.6.4.2 From 56c5290fcec619d45cb5fea00bbe483a852b4d6f Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 9 Apr 2010 19:32:47 -0700 Subject: [PATCH 576/779] CI: message for some outstanding failures --- actionpack/test/controller/render_test.rb | 4 ++-- actionpack/test/controller/send_file_test.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 112e9ef..a1cbe4c 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -1590,7 +1590,7 @@ class EtagRenderTest < ActionController::TestCase def test_render_blank_body_shouldnt_set_etag get :blank_response - assert !@response.etag? + assert !@response.etag?, @response.etag.inspect end def test_render_200_should_set_etag @@ -1609,7 +1609,7 @@ class EtagRenderTest < ActionController::TestCase def test_render_against_etag_request_should_have_no_content_length_when_match @request.if_none_match = etag_for("hello david") get :render_hello_world_from_variable - assert !@response.headers.has_key?("Content-Length"), @response.headers['Content-Length'] + assert !@response.headers.has_key?("Content-Length"), @response.headers['Content-Length'].inspect end def test_render_against_etag_request_should_200_when_no_match diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index f61bbc6..e316d36 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -73,7 +73,7 @@ class SendFileTest < ActionController::TestCase assert_equal @controller.file_path, response.headers['X-Sendfile'] assert response.body.blank? - assert !response.etag? + assert !response.etag?, response.etag.inspect end def test_data -- 1.6.4.2 From efec9b24db3e454e7e5f6fa03de71ba76e9d75f8 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 9 Apr 2010 19:33:42 -0700 Subject: [PATCH 577/779] Refactor for readability --- activesupport/lib/active_support/ordered_hash.rb | 54 ++++++++++------------ 1 files changed, 24 insertions(+), 30 deletions(-) diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 57ead35..e1a2866 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -1,13 +1,28 @@ require 'yaml' +YAML.add_builtin_type("omap") do |type, val| + ActiveSupport::OrderedHash[val.map(&:to_a).map(&:first)] +end + # OrderedHash is namespaced to prevent conflicts with other implementations module ActiveSupport - # Hash is ordered in Ruby 1.9! - if RUBY_VERSION >= '1.9' - class OrderedHash < ::Hash #:nodoc: + class OrderedHash < ::Hash #:nodoc: + def to_yaml_type + "!tag:yaml.org,2002:omap" end - else - class OrderedHash < Hash #:nodoc: + + def to_yaml(opts = {}) + YAML.quick_emit(self, opts) do |out| + out.seq(taguri, to_yaml_style) do |seq| + each do |k, v| + seq.add(k => v) + end + end + end + end + + # Hash is ordered in Ruby 1.9! + if RUBY_VERSION < '1.9' def initialize(*args, &block) super @keys = [] @@ -55,7 +70,7 @@ module ActiveSupport end super end - + def delete_if super sync_keys! @@ -134,31 +149,10 @@ module ActiveSupport "#" end - private - - def sync_keys! - @keys.delete_if {|k| !has_key?(k)} - end - end - end - - class OrderedHash #:nodoc: - def to_yaml_type - "!tag:yaml.org,2002:omap" - end - - def to_yaml(opts = {}) - YAML.quick_emit(self, opts) do |out| - out.seq(taguri, to_yaml_style) do |seq| - each do |k, v| - seq.add(k => v) - end + private + def sync_keys! + @keys.delete_if {|k| !has_key?(k)} end - end end end - - YAML.add_builtin_type("omap") do |type, val| - ActiveSupport::OrderedHash[val.map(&:to_a).map(&:first)] - end end -- 1.6.4.2 From d91d6fe15f14f089349472f9b1422c4ef0f4c4d9 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 9 Apr 2010 20:06:35 -0700 Subject: [PATCH 578/779] CI: show all headers for diagnosis --- actionpack/test/controller/render_test.rb | 4 ++-- actionpack/test/controller/send_file_test.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index a1cbe4c..4538110 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -1590,7 +1590,7 @@ class EtagRenderTest < ActionController::TestCase def test_render_blank_body_shouldnt_set_etag get :blank_response - assert !@response.etag?, @response.etag.inspect + assert !@response.etag?, @response.headers.inspect end def test_render_200_should_set_etag @@ -1609,7 +1609,7 @@ class EtagRenderTest < ActionController::TestCase def test_render_against_etag_request_should_have_no_content_length_when_match @request.if_none_match = etag_for("hello david") get :render_hello_world_from_variable - assert !@response.headers.has_key?("Content-Length"), @response.headers['Content-Length'].inspect + assert !@response.headers.has_key?("Content-Length"), @response.headers.inspect end def test_render_against_etag_request_should_200_when_no_match diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index e316d36..a8e1afc 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -73,7 +73,7 @@ class SendFileTest < ActionController::TestCase assert_equal @controller.file_path, response.headers['X-Sendfile'] assert response.body.blank? - assert !response.etag?, response.etag.inspect + assert !response.etag?, response.headers.inspect end def test_data -- 1.6.4.2 From 2cd29f42979685188f4c248f716fc2e704e4a573 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 9 Apr 2010 20:19:03 -0700 Subject: [PATCH 579/779] Only set response etag if body is not blank --- actionpack/lib/action_controller/response.rb | 8 ++------ actionpack/test/controller/render_test.rb | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb index ec1922c..815f749 100644 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_controller/response.rb @@ -117,11 +117,7 @@ module ActionController # :nodoc: end def etag=(etag) - if etag.blank? - headers.delete('ETag') - else - headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}") - end + headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}") end def redirect(url, status) @@ -202,7 +198,7 @@ module ActionController # :nodoc: def nonempty_ok_response? ok = !status || status.to_s[0..2] == '200' - ok && body.is_a?(String) && !body.empty? + ok && body.is_a?(String) && !body.blank? end def set_conditional_cache_control! diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 4538110..331683e 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -1609,7 +1609,7 @@ class EtagRenderTest < ActionController::TestCase def test_render_against_etag_request_should_have_no_content_length_when_match @request.if_none_match = etag_for("hello david") get :render_hello_world_from_variable - assert !@response.headers.has_key?("Content-Length"), @response.headers.inspect + assert_nil @response.headers["Content-Length"], @response.headers.inspect end def test_render_against_etag_request_should_200_when_no_match -- 1.6.4.2 From dae247316d83c9c2f1b3cc797e0d6fcf98d46f92 Mon Sep 17 00:00:00 2001 From: Vicki Ball Date: Tue, 22 Sep 2009 19:06:58 +1000 Subject: [PATCH 580/779] made error_message_on work by passing in the object name if there is no object [#3246 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_view/helpers/form_helper.rb | 2 +- actionpack/test/template/form_helper_test.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 87e90e0..cd190e9 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1042,7 +1042,7 @@ module ActionView end def error_message_on(method, *args) - @template.error_message_on(@object, method, *args) + @template.error_message_on(@object || @object_name, method, *args) end def error_messages(options = {}) diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index d0c619d..5b62674 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1220,6 +1220,22 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + + def test_default_form_builder_without_object + + form_for(:post) do |f| + concat f.error_message_on('author_name') + concat f.error_messages + end + + expected = %(
) + + %(
can't be empty
) + + %(

1 error prohibited this post from being saved

There were problems with the following fields:

  • Author name can't be empty
) + + %(
) + + assert_dom_equal expected, output_buffer + + end # Perhaps this test should be moved to prototype helper tests. def test_remote_form_for_with_labelled_builder -- 1.6.4.2 From a84e9b4f31ac39b0e466f8f0efc507a52dbb02c0 Mon Sep 17 00:00:00 2001 From: Yaroslav Markin Date: Mon, 28 Dec 2009 01:58:14 +0300 Subject: [PATCH 581/779] Fix Array#to_xml to produce valid markup when working with namespaced classes [#3624 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_support/core_ext/array/conversions.rb | 2 +- activesupport/test/core_ext/array_ext_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 779743b..579b18c 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -164,7 +164,7 @@ module ActiveSupport #:nodoc: require 'builder' unless defined?(Builder) options = options.dup - options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records" + options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize.tr('/', '-') : "records" options[:children] ||= options[:root].singularize options[:indent] ||= 2 options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index fe48d39..7e8a8b3 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -300,6 +300,16 @@ class ArrayToXmlTests < Test::Unit::TestCase assert xml.include?(%(2)), xml end + + class Namespaced < Hash + end + def test_to_xml_with_namespaced_classes + xml = [ + Namespaced.new :name => "David" + ].to_xml + + assert_match(/ Date: Thu, 8 Apr 2010 22:08:33 +0530 Subject: [PATCH 582/779] Fix for plugin not getting installed on Windows environment [#4320 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../active_support/core_ext/kernel/reporting.rb | 2 +- railties/lib/commands/runner.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/kernel/reporting.rb b/activesupport/lib/active_support/core_ext/kernel/reporting.rb index 0f101e8..e2d5186 100644 --- a/activesupport/lib/active_support/core_ext/kernel/reporting.rb +++ b/activesupport/lib/active_support/core_ext/kernel/reporting.rb @@ -35,7 +35,7 @@ module Kernel # puts 'But this will' def silence_stream(stream) old_stream = stream.dup - stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null') + stream.reopen(RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'NUL:' : '/dev/null') stream.sync = true yield ensure diff --git a/railties/lib/commands/runner.rb b/railties/lib/commands/runner.rb index 5101283..5a4f244 100644 --- a/railties/lib/commands/runner.rb +++ b/railties/lib/commands/runner.rb @@ -18,7 +18,7 @@ ARGV.clone.options do |opts| opts.on("-h", "--help", "Show this help message.") { $stderr.puts opts; exit } - if RUBY_PLATFORM !~ /mswin/ + if RUBY_PLATFORM !~ /(:?mswin|mingw)/ opts.separator "" opts.separator "You can also use runner as a shebang line for your scripts like this:" opts.separator "-------------------------------------------------------------" -- 1.6.4.2 From 5efb1503dd88b59fe491dade92790c3f06293445 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 14 Apr 2010 01:51:43 +0100 Subject: [PATCH 583/779] Ensure not to load the entire association when bulk updating existing records using nested attributes --- .../associations/association_collection.rb | 20 ++++++++++---------- .../lib/active_record/nested_attributes.rb | 14 ++++++++++++-- activerecord/test/cases/nested_attributes_test.rb | 12 +++++++++++- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index f5c6aa4..a7e1516 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -407,6 +407,16 @@ module ActiveRecord records end + def add_record_to_target_with_callbacks(record) + callback(:before_add, record) + yield(record) if block_given? + @target ||= [] unless loaded? + @target << record unless @reflection.options[:uniq] && @target.include?(record) + callback(:after_add, record) + set_inverse_instance(record, @owner) + record + end + private def create_record(attrs) attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash) @@ -431,16 +441,6 @@ module ActiveRecord end end - def add_record_to_target_with_callbacks(record) - callback(:before_add, record) - yield(record) if block_given? - @target ||= [] unless loaded? - @target << record unless @reflection.options[:uniq] && @target.include?(record) - callback(:after_add, record) - set_inverse_instance(record, @owner) - record - end - def remove_records(*records) records = flatten_deeper(records) records.each { |record| raise_on_type_mismatch(record) } diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index ae24b80..9288239 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -340,14 +340,24 @@ module ActiveRecord attributes_collection = attributes_collection.sort_by { |index, _| index.to_i }.map { |_, attributes| attributes } end + association = send(association_name) + + existing_records = if association.loaded? + association.to_a + else + attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact + attribute_ids.present? ? association.all(:conditions => {:id => attribute_ids}) : [] + end + attributes_collection.each do |attributes| attributes = attributes.with_indifferent_access if attributes['id'].blank? unless reject_new_record?(association_name, attributes) - send(association_name).build(attributes.except(*UNASSIGNABLE_KEYS)) + association.build(attributes.except(*UNASSIGNABLE_KEYS)) end - elsif existing_record = send(association_name).detect { |record| record.id.to_s == attributes['id'].to_s } + elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s } + association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded? assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) else raise_nested_attributes_record_not_found(association_name, attributes['id']) diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 06000d5..9f8534c 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -452,6 +452,16 @@ module NestedAttributesOnACollectionAssociationTests assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name end + def test_should_not_load_association_when_updating_existing_records + @pirate.reload + @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }]) + assert ! @pirate.send(@association_name).loaded? + + @pirate.save + assert ! @pirate.send(@association_name).loaded? + assert_equal 'Grace OMalley', @child_1.reload.name + end + def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models @child_1.stubs(:id).returns('ABC1X') @child_2.stubs(:id).returns('ABC2X') @@ -506,7 +516,7 @@ module NestedAttributesOnACollectionAssociationTests def test_should_ignore_new_associated_records_if_a_reject_if_proc_returns_false @alternate_params[association_getter]['baz'] = {} - assert_no_difference("@pirate.send(@association_name).length") do + assert_no_difference("@pirate.send(@association_name).count") do @pirate.attributes = @alternate_params end end -- 1.6.4.2 From b8b568e53b90b919b3f47067aa124993b337768d Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Wed, 14 Apr 2010 11:57:36 -0700 Subject: [PATCH 584/779] fix dash -> edit in an example, and a few touches now that we are here --- actionpack/README | 14 +++++++------- 1 files changed, 7 insertions(+), 7 deletions(-) diff --git a/actionpack/README b/actionpack/README index e4ce4aa..1dff37e 100644 --- a/actionpack/README +++ b/actionpack/README @@ -155,20 +155,20 @@ A short rundown of the major features: map.connect 'clients/:client_name/:project_name/:controller/:action' Accessing /clients/37signals/basecamp/project/dash calls ProjectController#dash with - { "client_name" => "37signals", "project_name" => "basecamp" } in params[:params] + { "client_name" => "37signals", "project_name" => "basecamp" }. - From that URL, you can rewrite the redirect in a number of ways: + From that URL you can redirect providing new parameters in a number of ways: - redirect_to(:action => "edit") => - /clients/37signals/basecamp/project/dash + redirect_to :action => "edit" + # /clients/37signals/basecamp/project/edit - redirect_to(:client_name => "nextangle", :project_name => "rails") => - /clients/nextangle/rails/project/dash + redirect_to :client_name => "nextangle", :project_name => "rails" + # /clients/nextangle/rails/project/dash {Learn more}[link:classes/ActionController/Base.html] -* Javascript and Ajax integration +* JavaScript and Ajax integration link_to_function "Greeting", "alert('Hello world!')" link_to_remote "Delete this post", :update => "posts", -- 1.6.4.2 From 18ba648e0de032c6cb68cb48c2aaf3a347294e3d Mon Sep 17 00:00:00 2001 From: Eugene Pimenov Date: Thu, 2 Jul 2009 06:06:47 +0400 Subject: [PATCH 585/779] Implement find_in_batches without with_scope [#2227 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/batches.rb | 17 ++++++++--------- activerecord/test/cases/batches_test.rb | 6 ++++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/batches.rb b/activerecord/lib/active_record/batches.rb index 5a6cecd..99c4598 100644 --- a/activerecord/lib/active_record/batches.rb +++ b/activerecord/lib/active_record/batches.rb @@ -59,19 +59,18 @@ module ActiveRecord start = options.delete(:start).to_i batch_size = options.delete(:batch_size) || 1000 - with_scope(:find => options.merge(:order => batch_order, :limit => batch_size)) do - records = find(:all, :conditions => [ "#{table_name}.#{primary_key} >= ?", start ]) + proxy = scoped(options.merge(:order => batch_order, :limit => batch_size)) + records = proxy.find(:all, :conditions => [ "#{table_name}.#{primary_key} >= ?", start ]) - while records.any? - yield records + while records.any? + yield records - break if records.size < batch_size - records = find(:all, :conditions => [ "#{table_name}.#{primary_key} > ?", records.last.id ]) - end + break if records.size < batch_size + records = proxy.find(:all, :conditions => [ "#{table_name}.#{primary_key} > ?", records.last.id ]) end end - - + + private def batch_order "#{table_name}.#{primary_key} ASC" diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index 5009a90..68b9ffd 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -58,4 +58,10 @@ class EachTest < ActiveRecord::TestCase Post.find_in_batches(:batch_size => post_count + 1) {|batch| assert_kind_of Array, batch } end end + + def test_find_in_batches_doesnt_clog_conditions + Post.find_in_batches(:conditions => {:id => posts(:welcome).id}) do + assert_nothing_raised { Post.find(posts(:thinking).id) } + end + end end \ No newline at end of file -- 1.6.4.2 From c519215aa83b041d438772977f962f54fff81a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 14 Sep 2009 15:44:42 +0200 Subject: [PATCH 586/779] ruby 1.8.7 compat: `starts/ends_with?` doesn't cast to string `starts/ends_with?` methods shouldn't cast argument to string because ruby 1.8.7 doesn't seem to do that. for example: "foobar".ends_with?(:bar) # => true in ActiveSupport implementation, false in ruby 1.8.7 [#3199 state:committed] Signed-off-by: Jeremy Kemper --- .../core_ext/string/starts_ends_with.rb | 4 +--- activesupport/test/core_ext/string_ext_test.rb | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb b/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb index 09f9a18..bd4f39d 100644 --- a/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb +++ b/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb @@ -20,14 +20,12 @@ module ActiveSupport #:nodoc: # Does the string start with the specified +prefix+? def starts_with?(prefix) - prefix = prefix.to_s self[0, prefix.length] == prefix end # Does the string end with the specified +suffix+? def ends_with?(suffix) - suffix = suffix.to_s - self[-suffix.length, suffix.length] == suffix + self[-suffix.length, suffix.length] == suffix end end end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 2325103..084f861 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -177,6 +177,7 @@ class StringInflectionsTest < Test::Unit::TestCase s = "hello" assert s.starts_with?('h') assert s.starts_with?('hel') + assert !s.starts_with?(:hel) assert !s.starts_with?('el') assert s.start_with?('h') @@ -185,6 +186,7 @@ class StringInflectionsTest < Test::Unit::TestCase assert s.ends_with?('o') assert s.ends_with?('lo') + assert !s.ends_with?(:lo) assert !s.ends_with?('el') assert s.end_with?('o') -- 1.6.4.2 From c55cdd816c9067fd22497c94f598c7a8a59b7f49 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 16 Apr 2010 11:35:33 -0700 Subject: [PATCH 587/779] Distinguish test for 1.8.6 compat --- activesupport/test/core_ext/string_ext_test.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 084f861..eade30d 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -177,7 +177,7 @@ class StringInflectionsTest < Test::Unit::TestCase s = "hello" assert s.starts_with?('h') assert s.starts_with?('hel') - assert !s.starts_with?(:hel) + assert !s.starts_with?(:hel) if :hel.respond_to?(:length) assert !s.starts_with?('el') assert s.start_with?('h') @@ -186,7 +186,7 @@ class StringInflectionsTest < Test::Unit::TestCase assert s.ends_with?('o') assert s.ends_with?('lo') - assert !s.ends_with?(:lo) + assert !s.ends_with?(:lo) if :lo.respond_to?(:length) assert !s.ends_with?('el') assert s.end_with?('o') -- 1.6.4.2 From ef0591efc26a33c987e17c07d09036cf8112c693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Fri, 16 Apr 2010 23:31:38 +0200 Subject: [PATCH 588/779] cleanup `update/reset_counters`; refactor tests --- activerecord/lib/active_record/base.rb | 23 +++++----- activerecord/test/cases/base_test.rb | 49 --------------------- activerecord/test/cases/counter_cache_test.rb | 58 +++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 61 deletions(-) create mode 100755 activerecord/test/cases/counter_cache_test.rb diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 32d2854..54637f1 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -937,8 +937,13 @@ module ActiveRecord #:nodoc: counters.each do |association| child_class = reflect_on_association(association).klass counter_name = child_class.reflect_on_association(self.name.downcase.to_sym).counter_cache_column + value = object.send(association).count - connection.update("UPDATE #{quoted_table_name} SET #{connection.quote_column_name(counter_name)} = #{object.send(association).count} WHERE #{connection.quote_column_name(primary_key)} = #{quote_value(object.id)}", "#{name} UPDATE") + connection.update(<<-CMD, "#{name} UPDATE") + UPDATE #{quoted_table_name} + SET #{connection.quote_column_name(counter_name)} = #{value} + WHERE #{connection.quote_column_name(primary_key)} = #{quote_value(object.id)} + CMD end end @@ -972,19 +977,13 @@ module ActiveRecord #:nodoc: # # SET comment_count = comment_count + 1, # # WHERE id IN (10, 15) def update_counters(id, counters) - updates = counters.inject([]) { |list, (counter_name, increment)| - sign = increment < 0 ? "-" : "+" - list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}" - }.join(", ") - - if id.is_a?(Array) - ids_list = id.map {|i| quote_value(i)}.join(', ') - condition = "IN (#{ids_list})" - else - condition = "= #{quote_value(id)}" + updates = counters.map do |counter_name, value| + operator = value < 0 ? '-' : '+' + quoted_column = connection.quote_column_name(counter_name) + "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}" end - update_all(updates, "#{connection.quote_column_name(primary_key)} #{condition}") + update_all(updates.join(', '), primary_key => id ) end # Increment a number field by one, usually representing a count. diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 2bf7939..3524e00 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -612,55 +612,6 @@ class BasicsTest < ActiveRecord::TestCase assert Topic.find(2).approved? end - def test_increment_counter - Topic.increment_counter("replies_count", 1) - assert_equal 2, Topic.find(1).replies_count - - Topic.increment_counter("replies_count", 1) - assert_equal 3, Topic.find(1).replies_count - end - - def test_decrement_counter - Topic.decrement_counter("replies_count", 2) - assert_equal -1, Topic.find(2).replies_count - - Topic.decrement_counter("replies_count", 2) - assert_equal -2, Topic.find(2).replies_count - end - - def test_reset_counters - assert_equal 1, Topic.find(1).replies_count - - Topic.increment_counter("replies_count", 1) - assert_equal 2, Topic.find(1).replies_count - - Topic.reset_counters(1, :replies) - assert_equal 1, Topic.find(1).replies_count - end - - def test_update_counter - category = categories(:general) - assert_nil category.categorizations_count - assert_equal 2, category.categorizations.count - - Category.update_counters(category.id, "categorizations_count" => category.categorizations.count) - category.reload - assert_not_nil category.categorizations_count - assert_equal 2, category.categorizations_count - - Category.update_counters(category.id, "categorizations_count" => category.categorizations.count) - category.reload - assert_not_nil category.categorizations_count - assert_equal 4, category.categorizations_count - - category_2 = categories(:technology) - count_1, count_2 = (category.categorizations_count || 0), (category_2.categorizations_count || 0) - Category.update_counters([category.id, category_2.id], "categorizations_count" => 2) - category.reload; category_2.reload - assert_equal count_1 + 2, category.categorizations_count - assert_equal count_2 + 2, category_2.categorizations_count - end - def test_update_all assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'") assert_equal "bulk updated!", Topic.find(1).content diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb new file mode 100755 index 0000000..4f57de9 --- /dev/null +++ b/activerecord/test/cases/counter_cache_test.rb @@ -0,0 +1,58 @@ +require 'cases/helper' +require 'models/topic' +require 'models/reply' +require 'models/category' +require 'models/categorization' + +class CounterCacheTest < ActiveRecord::TestCase + fixtures :topics, :categories, :categorizations + + test "increment counter" do + topic = Topic.find(1) + assert_difference 'topic.reload.replies_count' do + Topic.increment_counter(:replies_count, topic.id) + end + end + + test "decrement counter" do + topic = Topic.find(1) + assert_difference 'topic.reload.replies_count', -1 do + Topic.decrement_counter(:replies_count, topic.id) + end + end + + test "reset counters" do + topic = Topic.find(1) + # throw the count off by 1 + Topic.increment_counter(:replies_count, topic.id) + + # check that it gets reset + assert_difference 'topic.reload.replies_count', -1 do + Topic.reset_counters(topic.id, :replies) + end + end + + test "update counter with initial null value" do + category = categories(:general) + assert_equal 2, category.categorizations.count + assert_nil category.categorizations_count + + Category.update_counters(category.id, :categorizations_count => category.categorizations.count) + assert_equal 2, category.reload.categorizations_count + end + + test "update counter for decrement" do + topic = Topic.find(1) + assert_difference 'topic.reload.replies_count', -3 do + Topic.update_counters(topic.id, :replies_count => -3) + end + end + + test "update counters of multiple records" do + t1, t2 = topics(:first, :second) + + assert_difference ['t1.reload.replies_count', 't2.reload.replies_count'], 2 do + Topic.update_counters([t1.id, t2.id], :replies_count => 2) + end + end +end -- 1.6.4.2 From 8be3e09fcfb26c181f458e8d3e333b3987bd5c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Sat, 17 Apr 2010 02:01:38 +0200 Subject: [PATCH 589/779] fix `reset_counters` to work even with complex class names e.g. it guesses that a belongs_to association to Namespace::MyModel is named "my_model", unlike before where it would look up an association named "namespace::mymodel" and fail. --- activerecord/lib/active_record/base.rb | 6 +++- activerecord/test/cases/counter_cache_test.rb | 26 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 54637f1..faa8343 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -935,8 +935,9 @@ module ActiveRecord #:nodoc: def reset_counters(id, *counters) object = find(id) counters.each do |association| - child_class = reflect_on_association(association).klass - counter_name = child_class.reflect_on_association(self.name.downcase.to_sym).counter_cache_column + child_class = reflect_on_association(association.to_sym).klass + belongs_name = self.name.demodulize.underscore.to_sym + counter_name = child_class.reflect_on_association(belongs_name).counter_cache_column value = object.send(association).count connection.update(<<-CMD, "#{name} UPDATE") @@ -945,6 +946,7 @@ module ActiveRecord #:nodoc: WHERE #{connection.quote_column_name(primary_key)} = #{quote_value(object.id)} CMD end + return true end # A generic "counter updater" implementation, intended primarily to be diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb index 4f57de9..acfd28c 100755 --- a/activerecord/test/cases/counter_cache_test.rb +++ b/activerecord/test/cases/counter_cache_test.rb @@ -7,6 +7,14 @@ require 'models/categorization' class CounterCacheTest < ActiveRecord::TestCase fixtures :topics, :categories, :categorizations + class SpecialTopic < ::Topic + has_many :special_replies, :foreign_key => 'parent_id' + end + + class SpecialReply < ::Reply + belongs_to :special_topic, :foreign_key => 'parent_id', :counter_cache => 'replies_count' + end + test "increment counter" do topic = Topic.find(1) assert_difference 'topic.reload.replies_count' do @@ -31,6 +39,24 @@ class CounterCacheTest < ActiveRecord::TestCase Topic.reset_counters(topic.id, :replies) end end + + test "reset counters with string argument" do + topic = Topic.find(1) + Topic.increment_counter('replies_count', topic.id) + + assert_difference 'topic.reload.replies_count', -1 do + Topic.reset_counters(topic.id, 'replies') + end + end + + test "reset counters with modularized and camelized classnames" do + special = SpecialTopic.create!(:title => 'Special') + SpecialTopic.increment_counter(:replies_count, special.id) + + assert_difference 'special.reload.replies_count', -1 do + SpecialTopic.reset_counters(special.id, :special_replies) + end + end test "update counter with initial null value" do category = categories(:general) -- 1.6.4.2 From 4b36dafb35b99c570be853dc18595b3a43bd72a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Sat, 17 Apr 2010 08:48:16 +0200 Subject: [PATCH 590/779] `String#starts/ends_with?` should return false for non-string argument, not raise error Signed-off-by: Jeremy Kemper --- .../core_ext/string/starts_ends_with.rb | 4 ++-- activesupport/test/core_ext/string_ext_test.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb b/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb index bd4f39d..56701fd 100644 --- a/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb +++ b/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb @@ -20,12 +20,12 @@ module ActiveSupport #:nodoc: # Does the string start with the specified +prefix+? def starts_with?(prefix) - self[0, prefix.length] == prefix + prefix.respond_to?(:to_str) && self[0, prefix.length] == prefix end # Does the string end with the specified +suffix+? def ends_with?(suffix) - self[-suffix.length, suffix.length] == suffix + suffix.respond_to?(:to_str) && self[-suffix.length, suffix.length] == suffix end end end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index eade30d..084f861 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -177,7 +177,7 @@ class StringInflectionsTest < Test::Unit::TestCase s = "hello" assert s.starts_with?('h') assert s.starts_with?('hel') - assert !s.starts_with?(:hel) if :hel.respond_to?(:length) + assert !s.starts_with?(:hel) assert !s.starts_with?('el') assert s.start_with?('h') @@ -186,7 +186,7 @@ class StringInflectionsTest < Test::Unit::TestCase assert s.ends_with?('o') assert s.ends_with?('lo') - assert !s.ends_with?(:lo) if :lo.respond_to?(:length) + assert !s.ends_with?(:lo) assert !s.ends_with?('el') assert s.end_with?('o') -- 1.6.4.2 From 8b79c7c202fca443fd4d7a611900a5c2efbe08a9 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 17 Apr 2010 12:37:56 -0700 Subject: [PATCH 591/779] Fix 1.9-specific syntax error in test --- activesupport/test/core_ext/array_ext_test.rb | 5 +---- 1 files changed, 1 insertions(+), 4 deletions(-) diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index 7e8a8b3..f354ade 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -304,10 +304,7 @@ class ArrayToXmlTests < Test::Unit::TestCase class Namespaced < Hash end def test_to_xml_with_namespaced_classes - xml = [ - Namespaced.new :name => "David" - ].to_xml - + xml = [Namespaced.new(:name => "David")].to_xml assert_match(/ Date: Sat, 17 Apr 2010 12:38:16 -0700 Subject: [PATCH 592/779] Upgrade bundled i18n from 1.3.3 to 1.3.7 --- activesupport/lib/active_support/vendor.rb | 2 +- .../active_support/vendor/i18n-0.3.3/.gitignore | 3 - .../vendor/i18n-0.3.3/CHANGELOG.textile | 76 ----- .../active_support/vendor/i18n-0.3.3/MIT-LICENSE | 20 -- .../vendor/i18n-0.3.3/README.textile | 81 ----- .../lib/active_support/vendor/i18n-0.3.3/Rakefile | 24 -- .../vendor/i18n-0.3.3/benchmark/example.yml | 144 --------- .../vendor/i18n-0.3.3/benchmark/run.rb | 71 ---- .../vendor/i18n-0.3.3/contributors.txt | 17 - .../active_support/vendor/i18n-0.3.3/i18n.gemspec | 165 ---------- .../lib/active_support/vendor/i18n-0.3.3/init.rb | 1 - .../active_support/vendor/i18n-0.3.3/lib/i18n.rb | 277 ---------------- .../vendor/i18n-0.3.3/lib/i18n/backend.rb | 17 - .../i18n-0.3.3/lib/i18n/backend/active_record.rb | 70 ---- .../lib/i18n/backend/active_record/missing.rb | 67 ---- .../lib/i18n/backend/active_record/store_procs.rb | 38 --- .../lib/i18n/backend/active_record/translation.rb | 83 ----- .../vendor/i18n-0.3.3/lib/i18n/backend/base.rb | 259 --------------- .../vendor/i18n-0.3.3/lib/i18n/backend/cache.rb | 75 ----- .../vendor/i18n-0.3.3/lib/i18n/backend/cascade.rb | 44 --- .../vendor/i18n-0.3.3/lib/i18n/backend/chain.rb | 74 ----- .../i18n-0.3.3/lib/i18n/backend/fallbacks.rb | 52 --- .../vendor/i18n-0.3.3/lib/i18n/backend/fast.rb | 68 ---- .../vendor/i18n-0.3.3/lib/i18n/backend/gettext.rb | 75 ----- .../vendor/i18n-0.3.3/lib/i18n/backend/helpers.rb | 80 ----- .../lib/i18n/backend/interpolation_compiler.rb | 119 ------- .../vendor/i18n-0.3.3/lib/i18n/backend/metadata.rb | 73 ----- .../i18n-0.3.3/lib/i18n/backend/pluralization.rb | 57 ---- .../vendor/i18n-0.3.3/lib/i18n/backend/simple.rb | 22 -- .../lib/i18n/core_ext/object/meta_class.rb | 5 - .../lib/i18n/core_ext/string/interpolate.rb | 99 ------ .../vendor/i18n-0.3.3/lib/i18n/exceptions.rb | 61 ---- .../vendor/i18n-0.3.3/lib/i18n/gettext.rb | 25 -- .../vendor/i18n-0.3.3/lib/i18n/helpers.rb | 5 - .../vendor/i18n-0.3.3/lib/i18n/helpers/gettext.rb | 64 ---- .../vendor/i18n-0.3.3/lib/i18n/locale.rb | 6 - .../vendor/i18n-0.3.3/lib/i18n/locale/fallbacks.rb | 98 ------ .../vendor/i18n-0.3.3/lib/i18n/locale/tag.rb | 28 -- .../i18n-0.3.3/lib/i18n/locale/tag/parents.rb | 24 -- .../i18n-0.3.3/lib/i18n/locale/tag/rfc4646.rb | 76 ----- .../i18n-0.3.3/lib/i18n/locale/tag/simple.rb | 41 --- .../vendor/i18n-0.3.3/lib/i18n/version.rb | 3 - .../active_support/vendor/i18n-0.3.3/test/all.rb | 8 - .../vendor/i18n-0.3.3/test/api/basics.rb | 15 - .../vendor/i18n-0.3.3/test/api/defaults.rb | 40 --- .../vendor/i18n-0.3.3/test/api/interpolation.rb | 92 ------ .../vendor/i18n-0.3.3/test/api/link.rb | 55 ---- .../i18n-0.3.3/test/api/localization/date.rb | 91 ------ .../i18n-0.3.3/test/api/localization/date_time.rb | 90 ------ .../i18n-0.3.3/test/api/localization/procs.rb | 54 ---- .../i18n-0.3.3/test/api/localization/time.rb | 84 ----- .../vendor/i18n-0.3.3/test/api/lookup.rb | 45 --- .../vendor/i18n-0.3.3/test/api/pluralization.rb | 35 -- .../vendor/i18n-0.3.3/test/api/procs.rb | 40 --- .../test/cases/api/active_record_test.rb | 29 -- .../i18n-0.3.3/test/cases/api/all_features_test.rb | 40 --- .../i18n-0.3.3/test/cases/api/cascade_test.rb | 31 -- .../vendor/i18n-0.3.3/test/cases/api/chain_test.rb | 26 -- .../i18n-0.3.3/test/cases/api/fallbacks_test.rb | 33 -- .../vendor/i18n-0.3.3/test/cases/api/fast_test.rb | 31 -- .../test/cases/api/pluralization_test.rb | 33 -- .../i18n-0.3.3/test/cases/api/simple_test.rb | 21 -- .../cases/backend/active_record/missing_test.rb | 60 ---- .../test/cases/backend/active_record_test.rb | 52 --- .../i18n-0.3.3/test/cases/backend/cache_test.rb | 72 ----- .../i18n-0.3.3/test/cases/backend/cascade_test.rb | 66 ---- .../i18n-0.3.3/test/cases/backend/chain_test.rb | 64 ---- .../test/cases/backend/fallbacks_test.rb | 57 ---- .../i18n-0.3.3/test/cases/backend/fast_test.rb | 50 --- .../i18n-0.3.3/test/cases/backend/helpers_test.rb | 26 -- .../cases/backend/interpolation_compiler_test.rb | 107 ------- .../i18n-0.3.3/test/cases/backend/metadata_test.rb | 67 ---- .../test/cases/backend/pluralization_test.rb | 43 --- .../i18n-0.3.3/test/cases/backend/simple_test.rb | 77 ----- .../test/cases/core_ext/string/interpolate_test.rb | 94 ------ .../i18n-0.3.3/test/cases/gettext/api_test.rb | 201 ------------ .../i18n-0.3.3/test/cases/gettext/backend_test.rb | 91 ------ .../i18n-0.3.3/test/cases/i18n_exceptions_test.rb | 97 ------ .../i18n-0.3.3/test/cases/i18n_load_path_test.rb | 23 -- .../vendor/i18n-0.3.3/test/cases/i18n_test.rb | 172 ---------- .../i18n-0.3.3/test/cases/locale/fallbacks_test.rb | 126 -------- .../test/cases/locale/tag/rfc4646_test.rb | 143 --------- .../test/cases/locale/tag/simple_test.rb | 33 -- .../vendor/i18n-0.3.3/test/fixtures/locales/de.po | 72 ----- .../vendor/i18n-0.3.3/test/fixtures/locales/en.rb | 3 - .../vendor/i18n-0.3.3/test/fixtures/locales/en.yml | 3 - .../i18n-0.3.3/test/fixtures/locales/plurals.rb | 113 ------- .../vendor/i18n-0.3.3/test/test_helper.rb | 100 ------ .../vendor/i18n-0.3.3/vendor/po_parser.rb | 329 ------------------- .../lib/active_support/vendor/i18n-0.3.7/i18n.rb | 335 ++++++++++++++++++++ .../vendor/i18n-0.3.7/i18n/backend.rb | 19 ++ .../i18n-0.3.7/i18n/backend/active_record.rb | 66 ++++ .../i18n/backend/active_record/missing.rb | 67 ++++ .../i18n/backend/active_record/store_procs.rb | 38 +++ .../i18n/backend/active_record/translation.rb | 88 +++++ .../vendor/i18n-0.3.7/i18n/backend/base.rb | 266 ++++++++++++++++ .../vendor/i18n-0.3.7/i18n/backend/cache.rb | 76 +++++ .../vendor/i18n-0.3.7/i18n/backend/cascade.rb | 58 ++++ .../vendor/i18n-0.3.7/i18n/backend/chain.rb | 75 +++++ .../vendor/i18n-0.3.7/i18n/backend/cldr.rb | 100 ++++++ .../vendor/i18n-0.3.7/i18n/backend/fallbacks.rb | 69 ++++ .../vendor/i18n-0.3.7/i18n/backend/fast.rb | 69 ++++ .../vendor/i18n-0.3.7/i18n/backend/gettext.rb | 75 +++++ .../vendor/i18n-0.3.7/i18n/backend/helpers.rb | 68 ++++ .../i18n/backend/interpolation_compiler.rb | 119 +++++++ .../vendor/i18n-0.3.7/i18n/backend/links.rb | 34 ++ .../vendor/i18n-0.3.7/i18n/backend/metadata.rb | 73 +++++ .../i18n-0.3.7/i18n/backend/pluralization.rb | 57 ++++ .../vendor/i18n-0.3.7/i18n/backend/simple.rb | 22 ++ .../vendor/i18n-0.3.7/i18n/core_ext/hash/except.rb | 8 + .../vendor/i18n-0.3.7/i18n/core_ext/hash/slice.rb | 8 + .../i18n-0.3.7/i18n/core_ext/object/meta_class.rb | 5 + .../i18n-0.3.7/i18n/core_ext/string/interpolate.rb | 99 ++++++ .../vendor/i18n-0.3.7/i18n/exceptions.rb | 61 ++++ .../vendor/i18n-0.3.7/i18n/gettext.rb | 25 ++ .../vendor/i18n-0.3.7/i18n/gettext/po_parser.rb | 329 +++++++++++++++++++ .../vendor/i18n-0.3.7/i18n/helpers.rb | 5 + .../vendor/i18n-0.3.7/i18n/helpers/gettext.rb | 65 ++++ .../vendor/i18n-0.3.7/i18n/locale.rb | 6 + .../vendor/i18n-0.3.7/i18n/locale/fallbacks.rb | 98 ++++++ .../vendor/i18n-0.3.7/i18n/locale/tag.rb | 28 ++ .../vendor/i18n-0.3.7/i18n/locale/tag/parents.rb | 24 ++ .../vendor/i18n-0.3.7/i18n/locale/tag/rfc4646.rb | 76 +++++ .../vendor/i18n-0.3.7/i18n/locale/tag/simple.rb | 41 +++ .../vendor/i18n-0.3.7/i18n/version.rb | 3 + 125 files changed, 2656 insertions(+), 5922 deletions(-) delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/.gitignore delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/CHANGELOG.textile delete mode 100755 activesupport/lib/active_support/vendor/i18n-0.3.3/MIT-LICENSE delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/README.textile delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/Rakefile delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/benchmark/example.yml delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/benchmark/run.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/contributors.txt delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/i18n.gemspec delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/init.rb delete mode 100755 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/missing.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/store_procs.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/translation.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/base.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/cache.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/cascade.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/chain.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/fallbacks.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/fast.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/gettext.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/helpers.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/interpolation_compiler.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/metadata.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/pluralization.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/simple.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/core_ext/object/meta_class.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/core_ext/string/interpolate.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/exceptions.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/gettext.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/helpers.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/helpers/gettext.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/fallbacks.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/parents.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/rfc4646.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/simple.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/version.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/all.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/basics.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/defaults.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/interpolation.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/link.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/date.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/date_time.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/procs.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/time.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/lookup.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/pluralization.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/procs.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/active_record_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/all_features_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/cascade_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/chain_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/fallbacks_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/fast_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/pluralization_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/simple_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/active_record/missing_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/active_record_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/cache_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/cascade_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/chain_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/fallbacks_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/fast_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/helpers_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/interpolation_compiler_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/metadata_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/pluralization_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/simple_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/core_ext/string/interpolate_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/gettext/api_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/gettext/backend_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_exceptions_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_load_path_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/fallbacks_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/tag/rfc4646_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/tag/simple_test.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/de.po delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/en.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/en.yml delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/plurals.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/test/test_helper.rb delete mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.3/vendor/po_parser.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/missing.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/store_procs.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/translation.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/base.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cache.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cascade.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/chain.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cldr.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/fallbacks.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/fast.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/gettext.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/helpers.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/interpolation_compiler.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/links.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/metadata.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/pluralization.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/simple.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/hash/except.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/hash/slice.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/object/meta_class.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/string/interpolate.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/exceptions.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/gettext.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/gettext/po_parser.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/helpers.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/helpers/gettext.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/fallbacks.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/parents.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/rfc4646.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/simple.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/version.rb diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index 4e06d5b..0194102 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -23,7 +23,7 @@ end begin gem 'i18n', '>= 0.3.3' rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.3.3/lib" + $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.3.7" end require 'i18n' diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/.gitignore b/activesupport/lib/active_support/vendor/i18n-0.3.3/.gitignore deleted file mode 100644 index 0f41a39..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.DS_Store -test/rails/fixtures -doc diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/CHANGELOG.textile b/activesupport/lib/active_support/vendor/i18n-0.3.3/CHANGELOG.textile deleted file mode 100644 index 462ff27..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/CHANGELOG.textile +++ /dev/null @@ -1,76 +0,0 @@ -h1. Changelog - -h2. master - -* (no changes) - -h2. 0.3.1 (2009-12-11) - -* "Add PoParser to gemspec":http://github.com/svenfuchs/i18n/commit/d6b2763f39c932f66adb039b96882a472f883c51 -* "Enable custom separators for ActiveRecord backend":http://github.com/svenfuchs/i18n/commit/9341d3fcfc951cc31807ba672d2b5d90909ef3e5 -* "Pass interpolation values to interpolation procs":http://github.com/svenfuchs/i18n/commit/39c2ed8fbad645671cd5520ce7ad0aeefe2b0cca -* "Fix that ngettext supports keys with dots":http://github.com/svenfuchs/i18n/commit/7362a43c34364d500de8899cfcca6bf1a5e6d1c8 - -h2. 0.3.0 (2009-11-30) - -* "Gettext backend and helpers":http://github.com/svenfuchs/i18n/commit/35a1740d2f10b808548af352006950da4017e374 -* "Metadata module":http://github.com/svenfuchs/i18n/commit/2677208555179b36fcbe958c0e8bc642cf5bc020 -* "Basic ActiveRecord backend":http://github.com/svenfuchs/i18n/commit/786632d0b42de423ecf0969622efc87f1691e2a2 -* "Set encoding to UTF8 for all files":http://github.com/svenfuchs/i18n/commit/9be3d4a311b5bf583eec5d39986176cc40c112f2 -* "Chain backend":http://github.com/svenfuchs/i18n/commit/08259ffb88b3005403648d77bc1cbca0b92f3cf5 -* "Backend/cache implementation":http://github.com/svenfuchs/i18n/commit/e7bf15351cd2e27f5972eb40e65a5dd6f4a0feed -* "Pluralization module":http://github.com/svenfuchs/i18n/commit/9ca4c9ed52d4706566a6abeb2d78722dcc5d4764 -* "add and adapt Globalize2 fallback implementation":http://github.com/svenfuchs/i18n/commit/1b37a303b27d6222b17162804b06323e5628768f -* "move Simple backend implementation to a Base backend class and extend Simple from Base.":http://github.com/svenfuchs/i18n/commit/32ddc80a04e6aa247f6d6613bde7f78c73396cb4 - -h2. 0.2.0 (2009-07-12) - -* "Allow using Ruby 1.9 syntax for string interpolation (API addition)":http://github.com/svenfuchs/i18n/commit/c6e0b06d512f2af57199a843a1d8a40241b32861 -* "Allow configuring the default scope separator, allow to pass a custom scope separator(API addition)":http://github.com/svenfuchs/i18n/commit/5b75bfbc348061adc11e3790187a187275bfd471 (e.g. I18n.t(:'foo|bar', :separator => '|') -* "Pass :format option to #translate for #localize more useful lambda support":http://github.com/svenfuchs/i18n/commit/e277711b3c844fe7589b8d3f9af0f7d1b969a273 -* "Refactor Simple backend #resolve to #default and #resolve for more consistency. Now allows to pass lambdas as defaults and re-resolve Symbols":http://github.com/svenfuchs/i18n/commit/8c4ce3d923ce5fa73e973fe28217e18165549aba -* "Add lambda support to #translate (API addition)":http://github.com/svenfuchs/i18n/commit/c90e62d8f7d3d5b78f34cfe328d871b58884f115 -* "Add lambda support to #localize (API addition)":http://github.com/svenfuchs/i18n/commit/9d390afcf33f3f469bb95e6888147152f6cc7442 - -h2. 0.1.3 (2009-02-27) - -* "Remove unnecessary string encoding handling in the i18n simple backend which made the backend break on Ruby 1.9":http://github.com/svenfuchs/i18n/commit/4c3a970783861a94f2e89f46714fb3434e4f4f8d - -h2. 0.1.2 (2009-01-09) - -* "added #available_locales (returns an array of locales for which translations are available)":http://github.com/svenfuchs/i18n/commit/411f8fe7c8f3f89e9b6b921fa62ed66cb92f3af4 -* "flatten load_path before using it so that a nested array of paths won't throw up":http://github.com/svenfuchs/i18n/commit/d473a068a2b90aba98135deb225d6eb6d8104d70 - -h2. 0.1.1 (2008-11-20) - -* "Use :'en' as a default locale (in favor of :'en-US')":http://github.com/svenfuchs/i18n/commit/c4b10b246aecf7da78cb2568dd0d2ab7e6b8a230 -* "Add #reload! to Simple backend":http://github.com/svenfuchs/i18n/commit/36dd2bd9973b9e1559728749a9daafa44693e964 - -h2. 0.1.0 (2008-10-25) - -* "Fix Simple backend to distinguish false from nil values":http://github.com/svenfuchs/i18n/commit/39d9a47da14b5f3ba126af48923af8c30e135166 -* "Add #load_path to public api, add initialize to simple backend and remove #load_translations from public api":http://github.com/svenfuchs/i18n/commit/c4c5649e6bc8f020f1aaf5a5470bde048e22c82d -* "Speed up Backend::Simple#interpolate":http://github.com/svenfuchs/i18n/commit/9e1ac6bf8833304e036323ec9932b9f33c468a35 -* "Remove #populate and #store_translations from public API":http://github.com/svenfuchs/i18n/commit/f4e514a80be7feb509f66824ee311905e2940900 -* "Use :other instead of :many as a plural key":http://github.com/svenfuchs/i18n/commit/0f8f20a2552bf6a2aa758d8fdd62a7154e4a1bf6 -* "Use a class instead of a module for Simple backend":http://github.com/svenfuchs/i18n/commit/08f051aa61320c17debde24a83268bc74e33b995 -* "Make Simple backend #interpolate deal with non-ASCII string encodings":http://github.com/svenfuchs/i18n/commit/d84a3f3f55543c084d5dc5d1fed613b8df148789 -* "Fix default arrays of non-existant keys returning the default array":http://github.com/svenfuchs/i18n/commit/6c04ca86c87f97dc78f07c2a4023644e5ba8b839 - -h2. Initial implementation (June/July 2008) - -Initial implementation by "Sven Fuchs":http://www.workingwithrails.com/person/9963-sven-fuchs based on previous discussion/consensus of the rails-i18n team (alphabetical order) and many others: - -* "Matt Aimonetti":http://railsontherun.com -* "Sven Fuchs":http://www.workingwithrails.com/person/9963-sven-fuchs -* "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey -* "Saimon Moore":http://saimonmoore.net -* "Stephan Soller":http://www.arkanis-development.de - -h2. More information - -* "Homepage":http://rails-i18n.org -* "Wiki":http://rails-i18n.org/wiki -* "Mailinglist":http://groups.google.com/group/rails-i18n -* "About the project/history":http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized -* "Initial API Intro":http://www.artweb-design.de/2008/7/18/the-ruby-on-rails-i18n-core-api \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/MIT-LICENSE b/activesupport/lib/active_support/vendor/i18n-0.3.3/MIT-LICENSE deleted file mode 100755 index ed8e9ee..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/MIT-LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2008 The Ruby I18n team - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/README.textile b/activesupport/lib/active_support/vendor/i18n-0.3.3/README.textile deleted file mode 100644 index faa9492..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/README.textile +++ /dev/null @@ -1,81 +0,0 @@ -h1. Ruby I18n - -Ruby Internationalization and localization solution. - -Features: - -* translation and localization -* interpolation of values to translations (Ruby 1.9 compatible syntax) -* pluralization (CLDR compatible) -* flexible defaults -* bulk lookup -* lambdas as translation data -* custom key/scope separator -* custom exception handlers -* extensible architecture with a swappable backend - -Pluggable features: - -* Cache -* Pluralization: lambda pluralizers stored as translation data -* Locale fallbacks, RFC4647 compliant (optionally: RFC4646 locale validation) -* Gettext support -* Translation metadata - -Alternative backends: - -* Chain -* ActiveRecord (optionally: ActiveRecordMissing) - -For more information and lots of resources see: "http://rails-i18n.org/wiki":http://rails-i18n.org/wiki - -h2. Installation - -gem install i18n - -h3. Installation on Rails < 2.3.5 (deprecated) - -Up to version 2.3.4 Rails will not accept i18n gems > 0.1.3. There is an unpacked -gem inside of active_support/lib/vendor which gets loaded unless gem 'i18n', '~> 0.1.3'. -This requirement is relaxed in "6da03653":http://github.com/rails/rails/commit/6da03653 - -The new i18n gem can be loaded from vendor/plugins like this: - - def reload_i18n! - raise "Move to i18n version 0.2.0 or greater" if Rails.version > "2.3.4" - - $:.grep(/i18n/).each { |path| $:.delete(path) } - I18n::Backend.send :remove_const, "Simple" - $: << Rails.root.join('vendor', 'plugins', 'i18n', 'lib').to_s - end - -Then you can `reload_i18n!` inside an i18n initializer. - -h2. Authors - -* "Sven Fuchs":http://www.artweb-design.de -* "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey -* "Stephan Soller":http://www.arkanis-development.de -* "Saimon Moore":http://saimonmoore.net -* "Matt Aimonetti":http://railsontherun.com - -h2. Contributors - -* Akira Matsuda -* Andrew Briening -* Clemens Kofler -* Frederick Cheung -* Jeremy Kemper -* José Valim -* Lawrence Pit -* Luca Guidi -* M4SSIVE -* Marko Seppae -* Mathias Meyer -* Michael Lang -* Theo Cushion -* Yaroslav Markin - -h2. License - -MIT License. See the included MIT-LICENCE file. \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/Rakefile b/activesupport/lib/active_support/vendor/i18n-0.3.3/Rakefile deleted file mode 100644 index 8159271..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/Rakefile +++ /dev/null @@ -1,24 +0,0 @@ -task :default => [:test] - -task :test do - ruby "test/all.rb" -end - -require File.expand_path("lib/i18n/version", File.dirname(__FILE__)) - -begin - require 'jeweler' - Jeweler::Tasks.new do |s| - s.name = "i18n" - s.version = I18n::VERSION - s.rubyforge_project = "i18n" - s.summary = "New wave Internationalization support for Ruby" - s.email = "rails-i18n@googlegroups.com" - s.homepage = "http://rails-i18n.org" - s.description = "Add Internationalization support to your Ruby application." - s.authors = ['Sven Fuchs', 'Joshua Harvey', 'Matt Aimonetti', 'Stephan Soller', 'Saimon Moore'] - s.files = FileList["[A-Z]*", "{lib,test,vendor}/**/*"] - end -rescue LoadError - puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/benchmark/example.yml b/activesupport/lib/active_support/vendor/i18n-0.3.3/benchmark/example.yml deleted file mode 100644 index 2f5b349..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/benchmark/example.yml +++ /dev/null @@ -1,144 +0,0 @@ -en: - first: "First" - activerecord: - errors: - messages: - inclusion: "is not included in the list" - exclusion: "is reserved" - invalid: "is invalid" - confirmation: "doesn't match confirmation" - accepted: "must be accepted" - empty: "can't be empty" - blank: "can't be blank" - too_long: "is too long (maximum is {{count}} characters)" - too_short: "is too short (minimum is {{count}} characters)" - wrong_length: "is the wrong length (should be {{count}} characters)" - taken: "has already been taken" - not_a_number: "is not a number" - greater_than: "must be greater than {{count}}" - greater_than_or_equal_to: "must be greater than or equal to {{count}}" - equal_to: "must be equal to {{count}}" - less_than: "must be less than {{count}}" - less_than_or_equal_to: "must be less than or equal to {{count}}" - odd: "must be odd" - even: "must be even" - record_invalid: "Validation failed: {{errors}}" - - models: - user: - blank: "This is a custom blank message for {{model}}: {{attribute}}" - attributes: - login: - blank: "This is a custom blank message for User login" - - - models: - user: "Dude" - - attributes: - admins: - user: - login: "Handle" - - date: - formats: - # Use the strftime parameters for formats. - # When no format has been given, it uses default. - # You can provide other formats here if you like! - default: "%Y-%m-%d" - short: "%b %d" - long: "%B %d, %Y" - - day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] - abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat] - - # Don't forget the nil at the beginning; there's no such thing as a 0th month - month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December] - abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec] - # Used in date_select and datime_select. - order: [ :year, :month, :day ] - - time: - formats: - default: "%a, %d %b %Y %H:%M:%S %z" - short: "%d %b %H:%M" - long: "%B %d, %Y %H:%M" - am: "am" - pm: "pm" - - support: - array: - words_connector: ", " - two_words_connector: " and " - last_word_connector: ", and " - - activemodel: - errors: - messages: - inclusion: "is not included in the list" - exclusion: "is reserved" - invalid: "is invalid" - confirmation: "doesn't match confirmation" - accepted: "must be accepted" - empty: "can't be empty" - blank: "can't be blank" - too_long: "is too long (maximum is {{count}} characters)" - too_short: "is too short (minimum is {{count}} characters)" - wrong_length: "is the wrong length (should be {{count}} characters)" - taken: "has already been taken" - not_a_number: "is not a number" - greater_than: "must be greater than {{count}}" - greater_than_or_equal_to: "must be greater than or equal to {{count}}" - equal_to: "must be equal to {{count}}" - less_than: "must be less than {{count}}" - less_than_or_equal_to: "must be less than or equal to {{count}}" - odd: "must be odd" - even: "must be even" - record_invalid: "Validation failed: {{errors}}" - - models: - user: - blank: "This is a custom blank message for {{model}}: {{attribute}}" - attributes: - login: - blank: "This is a custom blank message for User login" - - models: - user: "Dude" - - attributes: - user: - login: "Handle" - - model_data: - date: - formats: - # Use the strftime parameters for formats. - # When no format has been given, it uses default. - # You can provide other formats here if you like! - default: "%Y-%m-%d" - short: "%b %d" - long: "%B %d, %Y" - - day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] - abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat] - - # Don't forget the nil at the beginning; there's no such thing as a 0th month - month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December] - abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec] - # Used in date_select and datime_select. - order: [ :year, :month, :day ] - - time: - formats: - default: "%a, %d %b %Y %H:%M:%S %z" - short: "%d %b %H:%M" - long: "%B %d, %Y %H:%M" - am: "am" - pm: "pm" - - support: - array: - words_connector: ", " - two_words_connector: " and " - last_word_connector: ", and " \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/benchmark/run.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/benchmark/run.rb deleted file mode 100644 index 8a66708..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/benchmark/run.rb +++ /dev/null @@ -1,71 +0,0 @@ -#! /usr/bin/ruby -$:.unshift File.expand_path("../lib", File.dirname(__FILE__)) - -require 'i18n' -require 'i18n/core_ext/object/meta_class' -require 'benchmark' -require 'yaml' - -# Load YAML example file -YAML_HASH = YAML.load_file(File.expand_path("example.yml", File.dirname(__FILE__))) - -# Create benchmark backends -def create_backend(*modules) - Class.new do - modules.unshift(:Base) - modules.each { |m| include I18n::Backend.const_get(m) } - end -end - -BACKENDS = [] -BACKENDS << (SimpleBackend = create_backend) -BACKENDS << (FastBackend = create_backend(:Fast)) -BACKENDS << (InterpolationBackend = create_backend(:InterpolationCompiler)) -BACKENDS << (FastInterpolBackend = create_backend(:Fast, :InterpolationCompiler)) - -# Hack Report to print ms -module Benchmark - def self.ms(label = "", width=20, &blk) # :yield: - print label.ljust(width) - res = Benchmark::measure(&blk) - print format("%10.6f ms\n", res.real * 1000) - res - end -end - -# Run! -BACKENDS.each do |backend| - I18n.backend = backend.new - puts "===> #{backend.name}\n\n" - - Benchmark.ms "store" do - I18n.backend.store_translations *(YAML_HASH.to_a.first) - I18n.backend.translate :en, :first - end - - Benchmark.ms "t (depth=3)" do - I18n.backend.translate :en, :"activerecord.models.user" - end - - Benchmark.ms "t (depth=5)" do - I18n.backend.translate :en, :"activerecord.attributes.admins.user.login" - end - - Benchmark.ms "t (depth=7)" do - I18n.backend.translate :en, :"activerecord.errors.models.user.attributes.login.blank" - end - - Benchmark.ms "t w/ default" do - I18n.backend.translate :en, :"activerecord.models.another", :default => "Another" - end - - Benchmark.ms "t w/ interpolation" do - I18n.backend.translate :en, :"activerecord.errors.models.user.blank", :model => "User", :attribute => "name" - end - - Benchmark.ms "t subtree" do - I18n.backend.translate :en, :"activerecord.errors.messages" - end - - puts -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/contributors.txt b/activesupport/lib/active_support/vendor/i18n-0.3.3/contributors.txt deleted file mode 100644 index 3f24c57..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/contributors.txt +++ /dev/null @@ -1,17 +0,0 @@ -Akira Matsuda -Andrew Briening -Clemens Kofler -Frederick Cheung -Jeremy Kemper -Josh Harvey -José Valim -Lawrence Pit -Luca Guidi -M4SSIVE -Marko Seppae -Mathias Meyer -Matt Aimonetti -Michael Lang -Sven Fuchs -Theo Cushion -Yaroslav Markin diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/i18n.gemspec b/activesupport/lib/active_support/vendor/i18n-0.3.3/i18n.gemspec deleted file mode 100644 index 46b4290..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/i18n.gemspec +++ /dev/null @@ -1,165 +0,0 @@ -# Generated by jeweler -# DO NOT EDIT THIS FILE DIRECTLY -# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command -# -*- encoding: utf-8 -*- - -Gem::Specification.new do |s| - s.name = %q{i18n} - s.version = "0.3.3" - - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = ["Sven Fuchs", "Joshua Harvey", "Matt Aimonetti", "Stephan Soller", "Saimon Moore"] - s.date = %q{2009-12-29} - s.description = %q{Add Internationalization support to your Ruby application.} - s.email = %q{rails-i18n@googlegroups.com} - s.extra_rdoc_files = [ - "README.textile" - ] - s.files = [ - "CHANGELOG.textile", - "MIT-LICENSE", - "README.textile", - "Rakefile", - "lib/i18n.rb", - "lib/i18n/backend.rb", - "lib/i18n/backend/active_record.rb", - "lib/i18n/backend/active_record/missing.rb", - "lib/i18n/backend/active_record/store_procs.rb", - "lib/i18n/backend/active_record/translation.rb", - "lib/i18n/backend/base.rb", - "lib/i18n/backend/cache.rb", - "lib/i18n/backend/cascade.rb", - "lib/i18n/backend/chain.rb", - "lib/i18n/backend/fallbacks.rb", - "lib/i18n/backend/fast.rb", - "lib/i18n/backend/gettext.rb", - "lib/i18n/backend/helpers.rb", - "lib/i18n/backend/interpolation_compiler.rb", - "lib/i18n/backend/metadata.rb", - "lib/i18n/backend/pluralization.rb", - "lib/i18n/backend/simple.rb", - "lib/i18n/core_ext/object/meta_class.rb", - "lib/i18n/core_ext/string/interpolate.rb", - "lib/i18n/exceptions.rb", - "lib/i18n/gettext.rb", - "lib/i18n/helpers.rb", - "lib/i18n/helpers/gettext.rb", - "lib/i18n/locale.rb", - "lib/i18n/locale/fallbacks.rb", - "lib/i18n/locale/tag.rb", - "lib/i18n/locale/tag/parents.rb", - "lib/i18n/locale/tag/rfc4646.rb", - "lib/i18n/locale/tag/simple.rb", - "lib/i18n/version.rb", - "test/all.rb", - "test/api/basics.rb", - "test/api/defaults.rb", - "test/api/interpolation.rb", - "test/api/link.rb", - "test/api/localization/date.rb", - "test/api/localization/date_time.rb", - "test/api/localization/procs.rb", - "test/api/localization/time.rb", - "test/api/lookup.rb", - "test/api/pluralization.rb", - "test/api/procs.rb", - "test/cases/api/active_record_test.rb", - "test/cases/api/all_features_test.rb", - "test/cases/api/cascade_test.rb", - "test/cases/api/chain_test.rb", - "test/cases/api/fallbacks_test.rb", - "test/cases/api/fast_test.rb", - "test/cases/api/pluralization_test.rb", - "test/cases/api/simple_test.rb", - "test/cases/backend/active_record/missing_test.rb", - "test/cases/backend/active_record_test.rb", - "test/cases/backend/cache_test.rb", - "test/cases/backend/cascade_test.rb", - "test/cases/backend/chain_test.rb", - "test/cases/backend/fallbacks_test.rb", - "test/cases/backend/fast_test.rb", - "test/cases/backend/helpers_test.rb", - "test/cases/backend/interpolation_compiler_test.rb", - "test/cases/backend/metadata_test.rb", - "test/cases/backend/pluralization_test.rb", - "test/cases/backend/simple_test.rb", - "test/cases/core_ext/string/interpolate_test.rb", - "test/cases/gettext/api_test.rb", - "test/cases/gettext/backend_test.rb", - "test/cases/i18n_exceptions_test.rb", - "test/cases/i18n_load_path_test.rb", - "test/cases/i18n_test.rb", - "test/cases/locale/fallbacks_test.rb", - "test/cases/locale/tag/rfc4646_test.rb", - "test/cases/locale/tag/simple_test.rb", - "test/fixtures/locales/de.po", - "test/fixtures/locales/en.rb", - "test/fixtures/locales/en.yml", - "test/fixtures/locales/plurals.rb", - "test/test_helper.rb", - "vendor/po_parser.rb" - ] - s.homepage = %q{http://rails-i18n.org} - s.rdoc_options = ["--charset=UTF-8"] - s.require_paths = ["lib"] - s.rubyforge_project = %q{i18n} - s.rubygems_version = %q{1.3.5} - s.summary = %q{New wave Internationalization support for Ruby} - s.test_files = [ - "test/all.rb", - "test/api/basics.rb", - "test/api/defaults.rb", - "test/api/interpolation.rb", - "test/api/link.rb", - "test/api/localization/date.rb", - "test/api/localization/date_time.rb", - "test/api/localization/procs.rb", - "test/api/localization/time.rb", - "test/api/lookup.rb", - "test/api/pluralization.rb", - "test/api/procs.rb", - "test/cases/api/active_record_test.rb", - "test/cases/api/all_features_test.rb", - "test/cases/api/cascade_test.rb", - "test/cases/api/chain_test.rb", - "test/cases/api/fallbacks_test.rb", - "test/cases/api/fast_test.rb", - "test/cases/api/pluralization_test.rb", - "test/cases/api/simple_test.rb", - "test/cases/backend/active_record/missing_test.rb", - "test/cases/backend/active_record_test.rb", - "test/cases/backend/cache_test.rb", - "test/cases/backend/cascade_test.rb", - "test/cases/backend/chain_test.rb", - "test/cases/backend/fallbacks_test.rb", - "test/cases/backend/fast_test.rb", - "test/cases/backend/helpers_test.rb", - "test/cases/backend/interpolation_compiler_test.rb", - "test/cases/backend/metadata_test.rb", - "test/cases/backend/pluralization_test.rb", - "test/cases/backend/simple_test.rb", - "test/cases/core_ext/string/interpolate_test.rb", - "test/cases/gettext/api_test.rb", - "test/cases/gettext/backend_test.rb", - "test/cases/i18n_exceptions_test.rb", - "test/cases/i18n_load_path_test.rb", - "test/cases/i18n_test.rb", - "test/cases/locale/fallbacks_test.rb", - "test/cases/locale/tag/rfc4646_test.rb", - "test/cases/locale/tag/simple_test.rb", - "test/fixtures/locales/en.rb", - "test/fixtures/locales/plurals.rb", - "test/test_helper.rb" - ] - - if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION - s.specification_version = 3 - - if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then - else - end - else - end -end - diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/init.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/init.rb deleted file mode 100644 index b32b79b..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/init.rb +++ /dev/null @@ -1 +0,0 @@ -require File.dirname(__FILE__) + '/lib/i18n' \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n.rb deleted file mode 100755 index e2a7bc0..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n.rb +++ /dev/null @@ -1,277 +0,0 @@ -# encoding: utf-8 - -# Authors:: Sven Fuchs (http://www.artweb-design.de), -# Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey), -# Stephan Soller (http://www.arkanis-development.de/), -# Saimon Moore (http://saimonmoore.net), -# Matt Aimonetti (http://railsontherun.com/) -# Copyright:: Copyright (c) 2008 The Ruby i18n Team -# License:: MIT -require 'i18n/exceptions' -require 'i18n/core_ext/string/interpolate' - -module I18n - autoload :Backend, 'i18n/backend' - autoload :Helpers, 'i18n/helpers' - autoload :Locale, 'i18n/locale' - - @@backend = nil - @@load_path = nil - @@default_locale = :en - @@default_separator = '.' - @@exception_handler = :default_exception_handler - - class << self - # Returns the current backend. Defaults to +Backend::Simple+. - def backend - @@backend ||= Backend::Simple.new - end - - # Sets the current backend. Used to set a custom backend. - def backend=(backend) - @@backend = backend - end - - # Returns the current default locale. Defaults to :'en' - def default_locale - @@default_locale - end - - # Sets the current default locale. Used to set a custom default locale. - def default_locale=(locale) - @@default_locale = locale.to_sym rescue nil - end - - # Returns the current locale. Defaults to I18n.default_locale. - def locale - Thread.current[:locale] ||= default_locale - end - - # Sets the current locale pseudo-globally, i.e. in the Thread.current hash. - def locale=(locale) - Thread.current[:locale] = locale.to_sym rescue nil - end - - # Returns an array of locales for which translations are available. - # Unless you explicitely set the these through I18n.available_locales= - # the call will be delegated to the backend and memoized on the I18n module. - def available_locales - @@available_locales ||= backend.available_locales - end - - # Sets the available locales. - def available_locales=(locales) - @@available_locales = locales - end - - # Returns the current default scope separator. Defaults to '.' - def default_separator - @@default_separator - end - - # Sets the current default scope separator. - def default_separator=(separator) - @@default_separator = separator - end - - # Sets the exception handler. - def exception_handler=(exception_handler) - @@exception_handler = exception_handler - end - - # Allow clients to register paths providing translation data sources. The - # backend defines acceptable sources. - # - # E.g. the provided SimpleBackend accepts a list of paths to translation - # files which are either named *.rb and contain plain Ruby Hashes or are - # named *.yml and contain YAML data. So for the SimpleBackend clients may - # register translation files like this: - # I18n.load_path << 'path/to/locale/en.yml' - def load_path - @@load_path ||= [] - end - - # Sets the load path instance. Custom implementations are expected to - # behave like a Ruby Array. - def load_path=(load_path) - @@load_path = load_path - end - - # Tells the backend to reload translations. Used in situations like the - # Rails development environment. Backends can implement whatever strategy - # is useful. - def reload! - backend.reload! - end - - # Translates, pluralizes and interpolates a given key using a given locale, - # scope, and default, as well as interpolation values. - # - # *LOOKUP* - # - # Translation data is organized as a nested hash using the upper-level keys - # as namespaces. E.g., ActionView ships with the translation: - # :date => {:formats => {:short => "%b %d"}}. - # - # Translations can be looked up at any level of this hash using the key argument - # and the scope option. E.g., in this example I18n.t :date - # returns the whole translations hash {:formats => {:short => "%b %d"}}. - # - # Key can be either a single key or a dot-separated key (both Strings and Symbols - # work). E.g., the short format can be looked up using both: - # I18n.t 'date.formats.short' - # I18n.t :'date.formats.short' - # - # Scope can be either a single key, a dot-separated key or an array of keys - # or dot-separated keys. Keys and scopes can be combined freely. So these - # examples will all look up the same short date format: - # I18n.t 'date.formats.short' - # I18n.t 'formats.short', :scope => 'date' - # I18n.t 'short', :scope => 'date.formats' - # I18n.t 'short', :scope => %w(date formats) - # - # *INTERPOLATION* - # - # Translations can contain interpolation variables which will be replaced by - # values passed to #translate as part of the options hash, with the keys matching - # the interpolation variable names. - # - # E.g., with a translation :foo => "foo {{bar}}" the option - # value for the key +bar+ will be interpolated into the translation: - # I18n.t :foo, :bar => 'baz' # => 'foo baz' - # - # *PLURALIZATION* - # - # Translation data can contain pluralized translations. Pluralized translations - # are arrays of singluar/plural versions of translations like ['Foo', 'Foos']. - # - # Note that I18n::Backend::Simple only supports an algorithm for English - # pluralization rules. Other algorithms can be supported by custom backends. - # - # This returns the singular version of a pluralized translation: - # I18n.t :foo, :count => 1 # => 'Foo' - # - # These both return the plural version of a pluralized translation: - # I18n.t :foo, :count => 0 # => 'Foos' - # I18n.t :foo, :count => 2 # => 'Foos' - # - # The :count option can be used both for pluralization and interpolation. - # E.g., with the translation - # :foo => ['{{count}} foo', '{{count}} foos'], count will - # be interpolated to the pluralized translation: - # I18n.t :foo, :count => 1 # => '1 foo' - # - # *DEFAULTS* - # - # This returns the translation for :foo or default if no translation was found: - # I18n.t :foo, :default => 'default' - # - # This returns the translation for :foo or the translation for :bar if no - # translation for :foo was found: - # I18n.t :foo, :default => :bar - # - # Returns the translation for :foo or the translation for :bar - # or default if no translations for :foo and :bar were found. - # I18n.t :foo, :default => [:bar, 'default'] - # - # *BULK LOOKUP* - # - # This returns an array with the translations for :foo and :bar. - # I18n.t [:foo, :bar] - # - # Can be used with dot-separated nested keys: - # I18n.t [:'baz.foo', :'baz.bar'] - # - # Which is the same as using a scope option: - # I18n.t [:foo, :bar], :scope => :baz - # - # *LAMBDAS* - # - # Both translations and defaults can be given as Ruby lambdas. Lambdas will be - # called and passed the key and options. - # - # E.g. assuming the key :salutation resolves to: - # lambda { |key, options| options[:gender] == 'm' ? "Mr. {{options[:name]}}" : "Mrs. {{options[:name]}}" } - # - # Then I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith". - # - # It is recommended to use/implement lambdas in an "idempotent" way. E.g. when - # a cache layer is put in front of I18n.translate it will generate a cache key - # from the argument values passed to #translate. Therefor your lambdas should - # always return the same translations/values per unique combination of argument - # values. - def translate(*args) - options = args.pop if args.last.is_a?(Hash) - key = args.shift - locale = options && options.delete(:locale) || I18n.locale - raises = options && options.delete(:raise) - backend.translate(locale, key, options || {}) - rescue I18n::ArgumentError => exception - raise exception if raises - handle_exception(exception, locale, key, options) - end - alias :t :translate - - def translate!(key, options = {}) - translate(key, options.merge( :raise => true )) - end - alias :t! :translate! - - # Localizes certain objects, such as dates and numbers to local formatting. - def localize(object, options = {}) - locale = options[:locale] || I18n.locale - format = options[:format] || :default - backend.localize(locale, object, format) - end - alias :l :localize - - # making these private until Ruby 1.9.2 can send to protected methods again - # see http://redmine.ruby-lang.org/repositories/revision/ruby-19?rev=24280 - private - - # Handles exceptions raised in the backend. All exceptions except for - # MissingTranslationData exceptions are re-raised. When a MissingTranslationData - # was caught and the option :raise is not set the handler returns an error - # message string containing the key/scope. - def default_exception_handler(exception, locale, key, options) - return exception.message if MissingTranslationData === exception - raise exception - end - - # Any exceptions thrown in translate will be sent to the @@exception_handler - # which can be a Symbol, a Proc or any other Object. - # - # If exception_handler is a Symbol then it will simply be sent to I18n as - # a method call. A Proc will simply be called. In any other case the - # method #call will be called on the exception_handler object. - # - # Examples: - # - # I18n.exception_handler = :default_exception_handler # this is the default - # I18n.default_exception_handler(exception, locale, key, options) # will be called like this - # - # I18n.exception_handler = lambda { |*args| ... } # a lambda - # I18n.exception_handler.call(exception, locale, key, options) # will be called like this - # - # I18n.exception_handler = I18nExceptionHandler.new # an object - # I18n.exception_handler.call(exception, locale, key, options) # will be called like this - def handle_exception(exception, locale, key, options) - case @@exception_handler - when Symbol - send(@@exception_handler, exception, locale, key, options) - else - @@exception_handler.call(exception, locale, key, options) - end - end - - # Merges the given locale, key and scope into a single array of keys. - # Splits keys that contain dots into multiple keys. Makes sure all - # keys are Symbols. - def normalize_translation_keys(locale, key, scope, separator = nil) - keys = [locale] + Array(scope) + Array(key) - keys = keys.map { |k| k.to_s.split(separator || I18n.default_separator) } - keys = keys.flatten - [''] - keys.map { |k| k.to_sym } - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend.rb deleted file mode 100644 index 08f2d14..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend.rb +++ /dev/null @@ -1,17 +0,0 @@ -module I18n - module Backend - autoload :ActiveRecord, 'i18n/backend/active_record' - autoload :Base, 'i18n/backend/base' - autoload :Cache, 'i18n/backend/cache' - autoload :Cascade, 'i18n/backend/cascade' - autoload :Chain, 'i18n/backend/chain' - autoload :Fallbacks, 'i18n/backend/fallbacks' - autoload :Fast, 'i18n/backend/fast' - autoload :Gettext, 'i18n/backend/gettext' - autoload :Helpers, 'i18n/backend/helpers' - autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler' - autoload :Metadata, 'i18n/backend/metadata' - autoload :Pluralization, 'i18n/backend/pluralization' - autoload :Simple, 'i18n/backend/simple' - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record.rb deleted file mode 100644 index 0e40b0b..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record.rb +++ /dev/null @@ -1,70 +0,0 @@ -require 'i18n/backend/base' -require 'i18n/backend/active_record/translation' - -# -# This backend reads translations from a Translations table in environment database. Note that the database -# will not automatically be prepopulated with missing keys. You can achieve this effect with the ActiveRecordMissing backend, -# as the following example shows: -# -# I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18.backend, I18n::Backend::ActiveRecordMissing.new) -# -module I18n - module Backend - class ActiveRecord - autoload :Missing, 'i18n/backend/active_record/missing' - autoload :StoreProcs, 'i18n/backend/active_record/store_procs' - autoload :Translation, 'i18n/backend/active_record/translation' - - include Base - - def reload! - end - - def store_translations(locale, data, options = {}) - separator = options[:separator] || I18n.default_separator - wind_keys(data, separator).each do |key, v| - Translation.locale(locale).lookup(expand_keys(key, separator), separator).delete_all - Translation.create(:locale => locale.to_s, :key => key.to_s, :value => v) - end - end - - def available_locales - begin - Translation.available_locales - rescue ::ActiveRecord::StatementInvalid - [] - end - end - - protected - - def lookup(locale, key, scope = [], separator = nil) - return unless key - - separator ||= I18n.default_separator - key = (Array(scope) + Array(key)).join(separator) - - result = Translation.locale(locale).lookup(key, separator).all - if result.empty? - return nil - elsif result.first.key == key - return result.first.value - else - chop_range = (key.size + separator.size)..-1 - result = result.inject({}) do |hash, r| - hash[r.key.slice(chop_range)] = r.value - hash - end - deep_symbolize_keys(unwind_keys(result, separator)) - end - end - - # For a key :'foo.bar.baz' return ['foo', 'foo.bar', 'foo.bar.baz'] - def expand_keys(key, separator = I18n.default_separator) - key.to_s.split(separator).inject([]) do |keys, key| - keys << [keys.last, key].compact.join(separator) - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/missing.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/missing.rb deleted file mode 100644 index 481cd76..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/missing.rb +++ /dev/null @@ -1,67 +0,0 @@ -# This extension stores translation stub records for missing translations to -# the database. -# -# This is useful if you have a web based translation tool. It will populate -# the database with untranslated keys as the application is being used. A -# translator can then go through these and add missing translations. -# -# Example usage: -# -# I18n::Backend::Chain.send(:include, I18n::Backend::ActiveRecord::Missing) -# I18n.backend = I18nChainBackend.new(I18n::Backend::ActiveRecord.new, I18n::Backend::Simple.new) -# -# Stub records for pluralizations will also be created for each key defined -# in i18n.plural.keys. -# -# For example: -# -# # en.yml -# en: -# i18n: -# plural: -# keys: [:zero, :one, :other] -# -# # pl.yml -# pl: -# i18n: -# plural: -# keys: [:zero, :one, :few, :other] -# -# It will also persist interpolation keys in Translation#interpolations so -# translators will be able to review and use them. -module I18n - module Backend - class ActiveRecord - module Missing - def store_default_translations(locale, key, options = {}) - count, scope, default, separator = options.values_at(:count, *Base::RESERVED_KEYS) - separator ||= I18n.default_separator - - keys = I18n.send(:normalize_translation_keys, locale, key, scope, separator)[1..-1] - key = keys.join(separator || I18n.default_separator) - - unless ActiveRecord::Translation.locale(locale).lookup(key, separator).exists? - interpolations = options.reject { |name, value| Base::RESERVED_KEYS.include?(name) }.keys - keys = count ? I18n.t('i18n.plural.keys', :locale => locale).map { |k| [key, k].join(separator) } : [key] - keys.each { |key| store_default_translation(locale, key, interpolations) } - end - end - - def store_default_translation(locale, key, interpolations) - translation = ActiveRecord::Translation.new :locale => locale.to_s, :key => key - translation.interpolations = interpolations - translation.save - end - - def translate(locale, key, options = {}) - super - - rescue I18n::MissingTranslationData => e - self.store_default_translations(locale, key, options) - - raise e - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/store_procs.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/store_procs.rb deleted file mode 100644 index 6e221bf..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/store_procs.rb +++ /dev/null @@ -1,38 +0,0 @@ -# This module is intended to be mixed into the ActiveRecord backend to allow -# storing Ruby Procs as translation values in the database. -# -# I18n.backend = I18n::Backend::ActiveRecord.new -# I18n::Backend::ActiveRecord::Translation.send(:include, I18n::Backend::ActiveRecord::StoreProcs) -# -# The StoreProcs module requires the ParseTree and ruby2ruby gems and therefor -# was extracted from the original backend. -# -# ParseTree is not compatible with Ruby 1.9. - -begin - require 'ruby2ruby' - require 'parse_tree' - require 'parse_tree_extensions' -rescue LoadError => e - puts "can't use StoreProcs because: #{e.message}" -end - -module I18n - module Backend - class ActiveRecord - module StoreProcs - def value=(v) - case v - when Proc - write_attribute(:value, v.to_ruby) - write_attribute(:is_proc, true) - else - write_attribute(:value, v) - end - end - - Translation.send(:include, self) unless RUBY_VERSION >= '1.9' - end - end - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/translation.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/translation.rb deleted file mode 100644 index ffd1083..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/active_record/translation.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'active_record' - -module I18n - module Backend - # ActiveRecord model used to store actual translations to the database. - # - # This model expects a table like the following to be already set up in - # your the database: - # - # create_table :translations do |t| - # t.string :locale - # t.string :key - # t.string :value - # t.boolean :is_proc, :default => false - # end - # - # This model supports to named scopes :locale and :lookup. The :locale - # scope simply adds a condition for a given locale: - # - # I18n::Backend::ActiveRecord::Translation.locale(:en).all - # # => all translation records that belong to the :en locale - # - # The :lookup scope adds a condition for looking up all translations - # that either start with the given keys (joined by an optionally given - # separator or I18n.default_separator) or that exactly have this key. - # - # # with translations present for :"foo.bar" and :"foo.baz" - # I18n::Backend::ActiveRecord::Translation.lookup(:foo) - # # => an array with both translation records :"foo.bar" and :"foo.baz" - # - # I18n::Backend::ActiveRecord::Translation.lookup([:foo, :bar]) - # I18n::Backend::ActiveRecord::Translation.lookup(:"foo.bar") - # # => an array with the translation record :"foo.bar" - # - # When the StoreProcs module was mixed into this model then Procs will - # be stored to the database as Ruby code and evaluated when :value is - # called. - # - # Translation = I18n::Backend::ActiveRecord::Translation - # Translation.create \ - # :locale => 'en' - # :key => 'foo' - # :value => lambda { |key, options| 'FOO' } - # Translation.find_by_locale_and_key('en', 'foo').value - # # => 'FOO' - class ActiveRecord - class Translation < ::ActiveRecord::Base - set_table_name 'translations' - attr_protected :is_proc, :interpolations - - serialize :value - serialize :interpolations, Array - - named_scope :locale, lambda { |locale| - { :conditions => { :locale => locale.to_s } } - } - - named_scope :lookup, lambda { |keys, *separator| - keys = Array(keys).map! { |key| key.to_s } - separator = separator.first || I18n.default_separator - { :conditions => ["`key` IN (?) OR `key` LIKE '#{keys.last}#{separator}%'", keys] } - } - - def self.available_locales - Translation.find(:all, :select => 'DISTINCT locale').map { |t| t.locale.to_sym } - end - - def interpolates?(key) - self.interpolations.include?(key) if self.interpolations - end - - def value - if is_proc - Kernel.eval read_attribute(:value) - else - value = read_attribute(:value) - value == 'f' ? false : value - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/base.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/base.rb deleted file mode 100644 index 2de78ae..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/base.rb +++ /dev/null @@ -1,259 +0,0 @@ -# encoding: utf-8 - -require 'yaml' - -module I18n - module Backend - module Base - include I18n::Backend::Helpers - - RESERVED_KEYS = [:scope, :default, :separator] - INTERPOLATION_SYNTAX_PATTERN = /(\\)?\{\{([^\}]+)\}\}/ - - # Accepts a list of paths to translation files. Loads translations from - # plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml - # for details. - def load_translations(*filenames) - filenames.each { |filename| load_file(filename) } - end - - # Stores translations for the given locale in memory. - # This uses a deep merge for the translations hash, so existing - # translations will be overwritten by new ones only at the deepest - # level of the hash. - def store_translations(locale, data, options = {}) - merge_translations(locale, data) - end - - def translate(locale, key, options = {}) - raise InvalidLocale.new(locale) unless locale - return key.map { |k| translate(locale, k, options) } if key.is_a?(Array) - - if options.empty? - entry = resolve(locale, key, lookup(locale, key), options) - raise(I18n::MissingTranslationData.new(locale, key, options)) if entry.nil? - else - count, scope, default, separator = options.values_at(:count, :scope, :default, :separator) - values = options.reject { |name, value| RESERVED_KEYS.include?(name) } - - entry = lookup(locale, key, scope, separator) - entry = entry.nil? && default ? default(locale, key, default, options) : resolve(locale, key, entry, options) - raise(I18n::MissingTranslationData.new(locale, key, options)) if entry.nil? - - entry = pluralize(locale, entry, count) if count - entry = interpolate(locale, entry, values) if values - end - - entry - end - - # Acts the same as +strftime+, but uses a localized version of the - # format string. Takes a key from the date/time formats translations as - # a format argument (e.g., :short in :'date.formats'). - def localize(locale, object, format = :default, options = {}) - raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime) - - if Symbol === format - key = format - type = object.respond_to?(:sec) ? 'time' : 'date' - format = lookup(locale, :"#{type}.formats.#{key}") - raise(MissingTranslationData.new(locale, key, options)) if format.nil? - end - - format = resolve(locale, object, format, options) - format = format.to_s.gsub(/%[aAbBp]/) do |match| - case match - when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday] - when '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday] - when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon] - when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon] - when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format) if object.respond_to? :hour - end - end - - object.strftime(format) - end - - def initialized? - @initialized ||= false - end - - # Returns an array of locales for which translations are available - # ignoring the reserved translation meta data key :i18n. - def available_locales - init_translations unless initialized? - translations.inject([]) do |locales, (locale, data)| - locales << locale unless (data.keys - [:i18n]).empty? - locales - end - end - - def reload! - @initialized = false - @translations = nil - end - - protected - def init_translations - load_translations(*I18n.load_path.flatten) - @initialized = true - end - - def translations - @translations ||= {} - end - - # Looks up a translation from the translations hash. Returns nil if - # eiher key is nil, or locale, scope or key do not exist as a key in the - # nested translations hash. Splits keys or scopes containing dots - # into multiple keys, i.e. currency.format is regarded the same as - # %w(currency format). - def lookup(locale, key, scope = [], separator = nil) - return unless key - init_translations unless initialized? - keys = I18n.send(:normalize_translation_keys, locale, key, scope, separator) - keys.inject(translations) do |result, key| - key = key.to_sym - return nil unless result.is_a?(Hash) && result.has_key?(key) - result = result[key] - result = resolve(locale, key, result, :separator => separator) if result.is_a?(Symbol) - result - end - end - - # Evaluates defaults. - # If given subject is an Array, it walks the array and returns the - # first translation that can be resolved. Otherwise it tries to resolve - # the translation directly. - def default(locale, object, subject, options = {}) - options = options.dup.reject { |key, value| key == :default } - case subject - when Array - subject.each do |item| - result = resolve(locale, object, item, options) and return result - end and nil - else - resolve(locale, object, subject, options) - end - end - - # Resolves a translation. - # If the given subject is a Symbol, it will be translated with the - # given options. If it is a Proc then it will be evaluated. All other - # subjects will be returned directly. - def resolve(locale, object, subject, options = nil) - case subject - when Symbol - I18n.translate(subject, (options || {}).merge(:locale => locale, :raise => true)) - when Proc - resolve(locale, object, subject.call(object, options), options = {}) - else - subject - end - rescue MissingTranslationData - nil - end - - # Picks a translation from an array according to English pluralization - # rules. It will pick the first translation if count is not equal to 1 - # and the second translation if it is equal to 1. Other backends can - # implement more flexible or complex pluralization rules. - def pluralize(locale, entry, count) - return entry unless entry.is_a?(Hash) and count - - key = :zero if count == 0 && entry.has_key?(:zero) - key ||= count == 1 ? :one : :other - raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key) - entry[key] - end - - # Interpolates values into a given string. - # - # interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X' - # # => "file test.txt opened by {{user}}" - # - # Note that you have to double escape the \\ when you want to escape - # the {{...}} key in a string (once for the string and once for the - # interpolation). - def interpolate(locale, string, values = {}) - return string unless string.is_a?(String) && !values.empty? - - preserve_encoding(string) do - s = string.gsub(INTERPOLATION_SYNTAX_PATTERN) do - escaped, key = $1, $2.to_sym - if escaped - "{{#{key}}}" - elsif RESERVED_KEYS.include?(key) - raise ReservedInterpolationKey.new(key, string) - else - "%{#{key}}" - end - end - - values.each do |key, value| - value = value.call(values) if interpolate_lambda?(value, s, key) - value = value.to_s unless value.is_a?(String) - values[key] = value - end - - s % values - end - - rescue KeyError => e - raise MissingInterpolationArgument.new(values, string) - end - - def preserve_encoding(string) - if string.respond_to?(:encoding) - encoding = string.encoding - result = yield - result.force_encoding(encoding) if result.respond_to?(:force_encoding) - result - else - yield - end - end - - # returns true when the given value responds to :call and the key is - # an interpolation placeholder in the given string - def interpolate_lambda?(object, string, key) - object.respond_to?(:call) && string =~ /%\{#{key}\}|%\<#{key}>.*?\d*\.?\d*[bBdiouxXeEfgGcps]\}/ - end - - # Loads a single translations file by delegating to #load_rb or - # #load_yml depending on the file extension and directly merges the - # data to the existing translations. Raises I18n::UnknownFileType - # for all other file extensions. - def load_file(filename) - type = File.extname(filename).tr('.', '').downcase - raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}") - data = send :"load_#{type}", filename # TODO raise a meaningful exception if this does not yield a Hash - data.each { |locale, d| merge_translations(locale, d) } - end - - # Loads a plain Ruby translations file. eval'ing the file must yield - # a Hash containing translation data with locales as toplevel keys. - def load_rb(filename) - eval(IO.read(filename), binding, filename) - end - - # Loads a YAML translations file. The data must have locales as - # toplevel keys. - def load_yml(filename) - YAML::load(IO.read(filename)) - end - - # Deep merges the given translations hash with the existing translations - # for the given locale - def merge_translations(locale, data) - locale = locale.to_sym - translations[locale] ||= {} - data = deep_symbolize_keys(data) - - # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809 - merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 } - translations[locale].merge!(data, &merger) - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/cache.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/cache.rb deleted file mode 100644 index 65ecebe..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/cache.rb +++ /dev/null @@ -1,75 +0,0 @@ -# encoding: utf-8 - -# This module allows you to easily cache all responses from the backend - thus -# speeding up the I18n aspects of your application quite a bit. -# -# To enable caching you can simply include the Cache module to the Simple -# backend - or whatever other backend you are using: -# -# I18n::Backend::Simple.send(:include, I18n::Backend::Cache) -# -# You will also need to set a cache store implementation that you want to use: -# -# I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) -# -# You can use any cache implementation you want that provides the same API as -# ActiveSupport::Cache (only the methods #fetch and #write are being used). -# -# The cache_key implementation assumes that you only pass values to -# I18n.translate that return a valid key from #hash (see -# http://www.ruby-doc.org/core/classes/Object.html#M000337). -module I18n - class << self - @@cache_store = nil - @@cache_namespace = nil - - def cache_store - @@cache_store - end - - def cache_store=(store) - @@cache_store = store - end - - def cache_namespace - @@cache_namespace - end - - def cache_namespace=(namespace) - @@cache_namespace = namespace - end - - def perform_caching? - !cache_store.nil? - end - end - - module Backend - module Cache - def translate(*args) - I18n.perform_caching? ? fetch(*args) { super } : super - end - - protected - - def fetch(*args, &block) - result = I18n.cache_store.fetch(cache_key(*args), &block) - raise result if result.is_a?(Exception) - result - rescue MissingTranslationData => exception - I18n.cache_store.write(cache_key(*args), exception) - raise exception - end - - def cache_key(*args) - # This assumes that only simple, native Ruby values are passed to I18n.translate. - # Also, in Ruby < 1.8.7 {}.hash != {}.hash - # (see http://paulbarry.com/articles/2009/09/14/why-rails-3-will-require-ruby-1-8-7) - # If args.inspect does not work for you for some reason, patches are very welcome :) - hash = RUBY_VERSION >= "1.8.7" ? args.hash : args.inspect - keys = ['i18n', I18n.cache_namespace, hash] - keys.compact.join('-') - end - end - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/cascade.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/cascade.rb deleted file mode 100644 index edca8fc..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/cascade.rb +++ /dev/null @@ -1,44 +0,0 @@ -# encoding: utf-8 - -# EXPERIMENTAL -# -# The cascade module adds the ability to do cascading lookups to backends that -# are compatible to the Simple backend. -# -# By cascading lookups we mean that for any key that can not be found the -# Cascade module strips one segment off the scope part of the key and then -# tries to look up the key in that scope. -# -# E.g. when a lookup for the key :"foo.bar.baz" does not yield a result then -# the segment :bar will be stripped off the scope part :"foo.bar" and the new -# scope :foo will be used to look up the key :baz. If that does not succeed -# then the remaining scope segment :foo will be omitted, too, and again the -# key :baz will be looked up (now with no scope). -# -# Defaults will only kick in after the cascading lookups haven't succeeded. -# -# This behavior is useful for libraries like ActiveRecord validations where -# the library wants to give users a bunch of more or less fine-grained options -# of scopes for a particular key. -# -# Thanks to Clemens Kofler for the initial idea and implementation! See -# http://github.com/clemens/i18n-cascading-backend - -module I18n - @@fallbacks = nil - - module Backend - module Cascade - def lookup(locale, key, scope = [], separator = nil) - return unless key - locale, *scope = I18n.send(:normalize_translation_keys, locale, key, scope, separator) - key = scope.pop - - begin - result = super - return result unless result.nil? - end while scope.pop - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/chain.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/chain.rb deleted file mode 100644 index aef5d86..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/chain.rb +++ /dev/null @@ -1,74 +0,0 @@ -# encoding: utf-8 - -module I18n - module Backend - # Backend that chains multiple other backends and checks each of them when - # a translation needs to be looked up. This is useful when you want to use - # standard translations with a Simple backend but store custom application - # translations in a database or other backends. - # - # To use the Chain backend instantiate it and set it to the I18n module. - # You can add chained backends through the initializer or backends - # accessor: - # - # # preserves the existing Simple backend set to I18n.backend - # I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend) - # - # The implementation assumes that all backends added to the Chain implement - # a lookup method with the same API as Simple backend does. - class Chain < Simple - attr_accessor :backends - - def initialize(*backends) - self.backends = backends - end - - def reload! - backends.each { |backend| backend.reload! } - end - - def store_translations(locale, data, options = {}) - backends.first.store_translations(locale, data, options = {}) - end - - def available_locales - backends.map { |backend| backend.available_locales }.flatten.uniq - end - - def translate(locale, key, options = {}) - return key.map { |k| translate(locale, k, options) } if key.is_a?(Array) - - default = options.delete(:default) - namespace = {} - backends.each do |backend| - begin - options.update(:default => default) if default and backend == backends.last - translation = backend.translate(locale, key, options) - if namespace_lookup?(translation, options) - namespace.update(translation) - elsif !translation.nil? - return translation - end - rescue MissingTranslationData - end - end - return namespace unless namespace.empty? - raise(I18n::MissingTranslationData.new(locale, key, options)) - end - - def localize(locale, object, format = :default, options = {}) - backends.each do |backend| - begin - result = backend.localize(locale, object, format, options) and return result - rescue MissingTranslationData - end - end and nil - end - - protected - def namespace_lookup?(result, options) - result.is_a?(Hash) and not options.has_key?(:count) - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/fallbacks.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/fallbacks.rb deleted file mode 100644 index 774ecfa..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/fallbacks.rb +++ /dev/null @@ -1,52 +0,0 @@ -# encoding: utf-8 - -# I18n locale fallbacks are useful when you want your application to use -# translations from other locales when translations for the current locale are -# missing. E.g. you might want to use :en translations when translations in -# your applications main locale :de are missing. -# -# To enable locale fallbacks you can simply include the Fallbacks module to -# the Simple backend - or whatever other backend you are using: -# -# I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) -module I18n - @@fallbacks = nil - - class << self - # Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+. - def fallbacks - @@fallbacks ||= I18n::Locale::Fallbacks.new - end - - # Sets the current fallbacks implementation. Use this to set a different fallbacks implementation. - def fallbacks=(fallbacks) - @@fallbacks = fallbacks - end - end - - module Backend - module Fallbacks - # Overwrites the Base backend translate method so that it will try each - # locale given by I18n.fallbacks for the given locale. E.g. for the - # locale :"de-DE" it might try the locales :"de-DE", :de and :en - # (depends on the fallbacks implementation) until it finds a result with - # the given options. If it does not find any result for any of the - # locales it will then raise a MissingTranslationData exception as - # usual. - # - # The default option takes precedence over fallback locales, i.e. it - # will first evaluate a given default option before falling back to - # another locale. - def translate(locale, key, options = {}) - I18n.fallbacks[locale].each do |fallback| - begin - result = super(fallback, key, options) - return result unless result.nil? - rescue I18n::MissingTranslationData - end - end - raise(I18n::MissingTranslationData.new(locale, key, options)) - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/fast.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/fast.rb deleted file mode 100644 index 4a33a6e..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/fast.rb +++ /dev/null @@ -1,68 +0,0 @@ -# encoding: utf-8 - -# The Fast module contains optimizations that can tremendously speed up the -# lookup process on the Simple backend. It works by flattening the nested -# translation hash to a flat hash (e.g. { :a => { :b => 'c' } } becomes -# { :'a.b' => 'c' }). -# -# To enable these optimizations you can simply include the Fast module to -# the Simple backend: -# -# I18n::Backend::Simple.send(:include, I18n::Backend::Fast) -module I18n - module Backend - module Fast - SEPARATOR_ESCAPE_CHAR = "\001" - - def reset_flattened_translations! - @flattened_translations = nil - end - - def flattened_translations - @flattened_translations ||= flatten_translations(translations) - end - - def merge_translations(locale, data) - super - reset_flattened_translations! - end - - def init_translations - super - reset_flattened_translations! - end - - protected - # flatten_hash({:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}}) - # # => {:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}, :"b.f" => {:x=>"x"}, :"b.c"=>"c", :"b.f.x"=>"x", :"b.d"=>"d"} - def flatten_hash(h, nested_stack = [], flattened_h = {}, orig_h=h) - wind_keys(h, nil, true) - end - - def flatten_translations(translations) - # don't flatten locale roots - translations.inject({}) do |flattened_h, (locale_name, locale_translations)| - flattened_h[locale_name] = flatten_hash(locale_translations) - flattened_h - end - end - - def lookup(locale, key, scope = nil, separator = nil) - return unless key - init_translations unless initialized? - - if separator && I18n.default_separator != separator - key = cleanup_non_standard_separator(key, separator) - scope = Array(scope).map{|k| cleanup_non_standard_separator(k, separator)} if scope - end - - key = (Array(scope) + [key]).join(I18n.default_separator) if scope - flattened_translations[locale.to_sym][key.to_sym] - end - - def cleanup_non_standard_separator(key, user_separator) - escape_default_separator(key).tr(user_separator, I18n.default_separator) - end - end - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/gettext.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/gettext.rb deleted file mode 100644 index c2b81b2..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/gettext.rb +++ /dev/null @@ -1,75 +0,0 @@ -# encoding: utf-8 - -require 'i18n/gettext' -require File.expand_path(File.dirname(__FILE__) + '/../../../vendor/po_parser.rb') - -# Experimental support for using Gettext po files to store translations. -# -# To use this you can simply include the module to the Simple backend - or -# whatever other backend you are using. -# -# I18n::Backend::Simple.send(:include, I18n::Backend::Gettext) -# -# Now you should be able to include your Gettext translation (*.po) files to -# the I18n.load_path so they're loaded to the backend and you can use them as -# usual: -# -# I18n.load_path += Dir["path/to/locales/*.po"] -# -# Following the Gettext convention this implementation expects that your -# translation files are named by their locales. E.g. the file en.po would -# contain the translations for the English locale. -module I18n - module Backend - module Gettext - class PoData < Hash - def set_comment(msgid_or_sym, comment) - # ignore - end - end - - protected - def load_po(filename) - locale = ::File.basename(filename, '.po').to_sym - data = normalize(locale, parse(filename)) - { locale => data } - end - - def parse(filename) - GetText::PoParser.new.parse(::File.read(filename), PoData.new) - end - - def normalize(locale, data) - data.inject({}) do |result, (key, value)| - unless key.nil? || key.empty? - key, value = normalize_pluralization(locale, key, value) if key.index("\000") - - parts = key.split('|').reverse - normalized = parts.inject({}) do |normalized, part| - normalized = { part => normalized.empty? ? value : normalized } - end - - # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809 - merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 } - result.merge!(normalized, &merger) - end - result - end - end - - def normalize_pluralization(locale, key, value) - # FIXME po_parser includes \000 chars that can not be turned into Symbols - key = key.gsub("\000", I18n::Gettext::PLURAL_SEPARATOR).split(I18n::Gettext::PLURAL_SEPARATOR).first - - keys = I18n::Gettext.plural_keys(locale) - values = value.split("\000") - raise "invalid number of plurals: #{values.size}, keys: #{keys.inspect}" if values.size != keys.size - - result = {} - values.each_with_index { |value, ix| result[keys[ix]] = value } - [key, result] - end - - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/helpers.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/helpers.rb deleted file mode 100644 index eee2b73..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/helpers.rb +++ /dev/null @@ -1,80 +0,0 @@ -module I18n - module Backend - module Helpers - SEPARATOR_ESCAPE_CHAR = "\001" - - # Return a new hash with all keys and nested keys converted to symbols. - def deep_symbolize_keys(hash) - hash.inject({}) { |result, (key, value)| - value = deep_symbolize_keys(value) if value.is_a?(Hash) - result[(key.to_sym rescue key) || key] = value - result - } - end - - # Flatten keys for nested Hashes by chaining up keys using the separator - # >> { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}.wind - # => { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" } - def wind_keys(hash, separator = nil, subtree = false, prev_key = nil, result = {}, orig_hash=hash) - separator ||= I18n.default_separator - - hash.each_pair do |key, value| - key = escape_default_separator(key, separator) - curr_key = [prev_key, key].compact.join(separator).to_sym - - if value.is_a?(Symbol) - value = hash_lookup(orig_hash, value, separator) || - hash_lookup(hash, value, separator) || value - end - - if value.is_a?(Hash) - result[curr_key] = value if subtree - wind_keys(value, separator, subtree, curr_key, result, orig_hash) - else - result[curr_key] = value - end - end - - result - end - - def escape_default_separator(key, separator=nil) - key.to_s.tr(separator || I18n.default_separator, SEPARATOR_ESCAPE_CHAR) - end - - def hash_lookup(hash, keys, separator = ".") - keys.to_s.split(separator).inject(hash) do |result, key| - key = key.to_sym - if result.respond_to?(:has_key?) and result.has_key?(key) - result[key] - else - return nil - end - end - end - - # Expand keys chained by the the given separator through nested Hashes - # >> { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" }.unwind - # => { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"} - def unwind_keys(hash, separator = ".") - result = {} - hash.each do |key, value| - keys = key.to_s.split(separator) - curr = result - curr = curr[keys.shift] ||= {} while keys.size > 1 - curr[keys.shift] = value - end - result - end - - # # Flatten the given array once - # def flatten_once(array) - # result = [] - # for element in array # a little faster than each - # result.push(*element) - # end - # result - # end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/interpolation_compiler.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/interpolation_compiler.rb deleted file mode 100644 index b3c9273..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/interpolation_compiler.rb +++ /dev/null @@ -1,119 +0,0 @@ -# encoding: utf-8 - -# The InterpolationCompiler module contains optimizations that can tremendously -# speed up the interpolation process on the Simple backend. -# -# It works by defining a pre-compiled method on stored translation Strings that -# already bring all the knowledge about contained interpolation variables etc. -# so that the actual recurring interpolation will be very fast. -# -# To enable pre-compiled interpolations you can simply include the -# InterpolationCompiler module to the Simple backend: -# -# I18n::Backend::Simple.send(:include, I18n::Backend::InterpolationCompiler) -module I18n - module Backend - module InterpolationCompiler - module Compiler - extend self - - TOKENIZER = /(\\\{\{[^\}]+\}\}|\{\{[^\}]+\}\})/ - INTERPOLATION_SYNTAX_PATTERN = /(\\)?(\{\{([^\}]+)\}\})/ - - def compile_if_an_interpolation(string) - if interpolated_str?(string) - string.instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def i18n_interpolate(v = {}) - "#{compiled_interpolation_body(string)}" - end - RUBY_EVAL - end - - string - end - - def interpolated_str?(str) - str.kind_of?(String) && str =~ INTERPOLATION_SYNTAX_PATTERN - end - - protected - # tokenize("foo {{bar}} baz \\{{buz}}") # => ["foo ", "{{bar}}", " baz ", "\\{{buz}}"] - def tokenize(str) - str.split(TOKENIZER) - end - - def compiled_interpolation_body(str) - tokenize(str).map do |token| - (matchdata = token.match(INTERPOLATION_SYNTAX_PATTERN)) ? handle_interpolation_token(token, matchdata) : escape_plain_str(token) - end.join - end - - def handle_interpolation_token(interpolation, matchdata) - escaped, pattern, key = matchdata.values_at(1, 2, 3) - escaped ? pattern : compile_interpolation_token(key.to_sym) - end - - def compile_interpolation_token(key) - "\#{#{interpolate_or_raise_missing(key)}}" - end - - def interpolate_or_raise_missing(key) - escaped_key = escape_key_sym(key) - Base::RESERVED_KEYS.include?(key) ? reserved_key(escaped_key) : interpolate_key(escaped_key) - end - - def interpolate_key(key) - [direct_key(key), nil_key(key), missing_key(key)].join('||') - end - - def direct_key(key) - "((t = v[#{key}]) && t.respond_to?(:call) ? t.call : t)" - end - - def nil_key(key) - "(v.has_key?(#{key}) && '')" - end - - def missing_key(key) - "raise(MissingInterpolationArgument.new(#{key}, self))" - end - - def reserved_key(key) - "raise(ReservedInterpolationKey.new(#{key}, self))" - end - - def escape_plain_str(str) - str.gsub(/"|\\|#/) {|x| "\\#{x}"} - end - - def escape_key_sym(key) - # rely on Ruby to do all the hard work :) - key.to_sym.inspect - end - end - - def interpolate(locale, string, values) - if string.respond_to?(:i18n_interpolate) - string.i18n_interpolate(values) - elsif values - super - else - string - end - end - - def merge_translations(locale, data) - compile_all_strings_in(data) - super - end - - protected - def compile_all_strings_in(data) - data.each_value do |value| - Compiler.compile_if_an_interpolation(value) - compile_all_strings_in(value) if value.kind_of?(Hash) - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/metadata.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/metadata.rb deleted file mode 100644 index 3ebb8af..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/metadata.rb +++ /dev/null @@ -1,73 +0,0 @@ -# I18n translation metadata is useful when you want to access information -# about how a translation was looked up, pluralized or interpolated in -# your application. -# -# msg = I18n.t(:message, :default => 'Hi!', :scope => :foo) -# msg.translation_metadata -# # => { :key => :message, :scope => :foo, :default => 'Hi!' } -# -# If a :count option was passed to #translate it will be set to the metadata. -# Likewise, if any interpolation variables were passed they will also be set. -# -# To enable translation metadata you can simply include the Metadata module -# into the Simple backend class - or whatever other backend you are using: -# -# I18n::Backend::Simple.send(:include, I18n::Backend::Metadata) - -require 'i18n/core_ext/object/meta_class' - -module I18n - module Backend - module Metadata - class << self - def included(base) - Object.class_eval do - def translation_metadata - @translation_metadata ||= {} - end - - def translation_metadata=(translation_metadata) - @translation_metadata = translation_metadata - end - end - end - end - - def translate(locale, key, options = {}) - metadata = { - :locale => locale, - :key => key, - :scope => options[:scope], - :default => options[:default], - :separator => options[:separator], - :values => options.reject { |name, value| Base::RESERVED_KEYS.include?(name) } - } - with_metadata(metadata) { super } - end - - def interpolate(locale, string, values = {}) - with_metadata(:original => string) do - preserve_translation_metadata(string) { super } - end if string - end - - def pluralize(locale, entry, count) - with_metadata(:count => count) { super } - end - - protected - - def with_metadata(metadata, &block) - result = yield - result.translation_metadata = result.translation_metadata.merge(metadata) if result - result - end - - def preserve_translation_metadata(object, &block) - result = yield - result.translation_metadata = object.translation_metadata if result - result - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/pluralization.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/pluralization.rb deleted file mode 100644 index f9ad94e..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/pluralization.rb +++ /dev/null @@ -1,57 +0,0 @@ -# encoding: utf-8 - -# I18n locale fallbacks are useful when you want your application to use -# translations from other locales when translations for the current locale are -# missing. E.g. you might want to use :en translations when translations in -# your applications main locale :de are missing. -# -# To enable locale specific pluralizations you can simply include the -# Pluralization module to the Simple backend - or whatever other backend you -# are using. -# -# I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization) -# -# You also need to make sure to provide pluralization algorithms to the -# backend, i.e. include them to your I18n.load_path accordingly. -module I18n - module Backend - module Pluralization - # Overwrites the Base backend translate method so that it will check the - # translation meta data space (:i18n) for a locale specific pluralization - # rule and use it to pluralize the given entry. I.e. the library expects - # pluralization rules to be stored at I18n.t(:'i18n.plural.rule') - # - # Pluralization rules are expected to respond to #call(entry, count) and - # return a pluralization key. Valid keys depend on the translation data - # hash (entry) but it is generally recommended to follow CLDR's style, - # i.e., return one of the keys :zero, :one, :few, :many, :other. - # - # The :zero key is always picked directly when count equals 0 AND the - # translation data has the key :zero. This way translators are free to - # either pick a special :zero translation even for languages where the - # pluralizer does not return a :zero key. - def pluralize(locale, entry, count) - return entry unless entry.is_a?(Hash) and count - - pluralizer = pluralizer(locale) - if pluralizer.respond_to?(:call) - key = count == 0 && entry.has_key?(:zero) ? :zero : pluralizer.call(count) - raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key) - entry[key] - else - super - end - end - - protected - - def pluralizers - @pluralizers ||= {} - end - - def pluralizer(locale) - pluralizers[locale] ||= lookup(locale, :'i18n.plural.rule') - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/simple.rb deleted file mode 100644 index 1454a10..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/backend/simple.rb +++ /dev/null @@ -1,22 +0,0 @@ -# encoding: utf-8 - -# Stub class for the Simple backend. The actual implementation is provided by -# the backend Base class. This makes it easier to extend the Simple backend's -# behaviour by including modules. E.g.: -# -# module I18n::Backend::Pluralization -# def pluralize(*args) -# # extended pluralization logic -# super -# end -# end -# -# I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization) - -module I18n - module Backend - class Simple - include Base - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/core_ext/object/meta_class.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/core_ext/object/meta_class.rb deleted file mode 100644 index e6d51f7..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/core_ext/object/meta_class.rb +++ /dev/null @@ -1,5 +0,0 @@ -Object.class_eval do - def meta_class - class << self; self; end - end -end unless Object.method_defined?(:meta_class) diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/core_ext/string/interpolate.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/core_ext/string/interpolate.rb deleted file mode 100644 index 0a8a5fa..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/core_ext/string/interpolate.rb +++ /dev/null @@ -1,99 +0,0 @@ -# encoding: utf-8 - -=begin - heavily based on Masao Mutoh's gettext String interpolation extension - http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb - Copyright (C) 2005-2009 Masao Mutoh - You may redistribute it and/or modify it under the same license terms as Ruby. -=end - -if RUBY_VERSION < '1.9' - - # KeyError is raised by String#% when the string contains a named placeholder - # that is not contained in the given arguments hash. Ruby 1.9 includes and - # raises this exception natively. We define it to mimic Ruby 1.9's behaviour - # in Ruby 1.8.x - - class KeyError < IndexError - def initialize(message = nil) - super(message || "key not found") - end - end unless defined?(KeyError) - - # Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError. - # - # String#% method which accept "named argument". The translator can know - # the meaning of the msgids using "named argument" instead of %s/%d style. - - class String - # For older ruby versions, such as ruby-1.8.5 - alias :bytesize :size unless instance_methods.find {|m| m.to_s == 'bytesize'} - alias :interpolate_without_ruby_19_syntax :% # :nodoc: - - INTERPOLATION_PATTERN = Regexp.union( - /%\{(\w+)\}/, # matches placeholders like "%{foo}" - /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%.d" - ) - - INTERPOLATION_PATTERN_WITH_ESCAPE = Regexp.union( - /%%/, - INTERPOLATION_PATTERN - ) - - # % uses self (i.e. the String) as a format specification and returns the - # result of applying it to the given arguments. In other words it interpolates - # the given arguments to the string according to the formats the string - # defines. - # - # There are three ways to use it: - # - # * Using a single argument or Array of arguments. - # - # This is the default behaviour of the String class. See Kernel#sprintf for - # more details about the format string. - # - # Example: - # - # "%d %s" % [1, "message"] - # # => "1 message" - # - # * Using a Hash as an argument and unformatted, named placeholders. - # - # When you pass a Hash as an argument and specify placeholders with %{foo} - # it will interpret the hash values as named arguments. - # - # Example: - # - # "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"} - # # => "Masao Mutoh" - # - # * Using a Hash as an argument and formatted, named placeholders. - # - # When you pass a Hash as an argument and specify placeholders with %d - # it will interpret the hash values as named arguments and format the value - # according to the formatting instruction appended to the closing >. - # - # Example: - # - # "%d, %.1f" % { :integer => 10, :float => 43.4 } - # # => "10, 43.3" - def %(args) - if args.kind_of?(Hash) - dup.gsub(INTERPOLATION_PATTERN_WITH_ESCAPE) do |match| - if match == '%%' - '%' - else - key = ($1 || $2).to_sym - raise KeyError unless args.has_key?(key) - $3 ? sprintf("%#{$3}", args[key]) : args[key] - end - end - elsif self =~ INTERPOLATION_PATTERN - raise ArgumentError.new('one hash required') - else - result = gsub(/%([{<])/, '%%\1') - result.send :'interpolate_without_ruby_19_syntax', args - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/exceptions.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/exceptions.rb deleted file mode 100644 index 79996a1..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/exceptions.rb +++ /dev/null @@ -1,61 +0,0 @@ -# encoding: utf-8 - -class KeyError < IndexError - def initialize(message = nil) - super(message || "key not found") - end -end unless defined?(KeyError) - -module I18n - class ArgumentError < ::ArgumentError; end - - class InvalidLocale < ArgumentError - attr_reader :locale - def initialize(locale) - @locale = locale - super "#{locale.inspect} is not a valid locale" - end - end - - class MissingTranslationData < ArgumentError - attr_reader :locale, :key, :options - def initialize(locale, key, opts = nil) - @key, @locale, @options = key, locale, opts || {} - keys = I18n.send(:normalize_translation_keys, locale, key, options[:scope]) - keys << 'no key' if keys.size < 2 - super "translation missing: #{keys.join(', ')}" - end - end - - class InvalidPluralizationData < ArgumentError - attr_reader :entry, :count - def initialize(entry, count) - @entry, @count = entry, count - super "translation data #{entry.inspect} can not be used with :count => #{count}" - end - end - - class MissingInterpolationArgument < ArgumentError - attr_reader :values, :string - def initialize(values, string) - @values, @string = values, string - super "missing interpolation argument in #{string.inspect} (#{values.inspect} given)" - end - end - - class ReservedInterpolationKey < ArgumentError - attr_reader :key, :string - def initialize(key, string) - @key, @string = key, string - super "reserved key #{key.inspect} used in #{string.inspect}" - end - end - - class UnknownFileType < ArgumentError - attr_reader :type, :filename - def initialize(type, filename) - @type, @filename = type, filename - super "can not load translations from #{filename}, the file type #{type} is not known" - end - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/gettext.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/gettext.rb deleted file mode 100644 index b8dec6e..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/gettext.rb +++ /dev/null @@ -1,25 +0,0 @@ -# encoding: utf-8 - -module I18n - module Gettext - PLURAL_SEPARATOR = "\001" - CONTEXT_SEPARATOR = "\004" - - @@plural_keys = { :en => [:one, :other] } - - class << self - # returns an array of plural keys for the given locale so that we can - # convert from gettext's integer-index based style - # TODO move this information to the pluralization module - def plural_keys(locale) - @@plural_keys[locale] || @@plural_keys[:en] - end - - def extract_scope(msgid, separator) - scope = msgid.to_s.split(separator) - msgid = scope.pop - [scope, msgid] - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/helpers.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/helpers.rb deleted file mode 100644 index 8bbe02e..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/helpers.rb +++ /dev/null @@ -1,5 +0,0 @@ -module I18n - module Helpers - autoload :Gettext, 'i18n/helpers/gettext' - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/helpers/gettext.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/helpers/gettext.rb deleted file mode 100644 index f44bbe6..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/helpers/gettext.rb +++ /dev/null @@ -1,64 +0,0 @@ -# encoding: utf-8 - -module I18n - module Helpers - # Implements classical Gettext style accessors. To use this include the - # module to the global namespace or wherever you want to use it. - # - # include I18n::Helpers::Gettext - module Gettext - def gettext(msgid, options = {}) - I18n.t(msgid, { :default => msgid, :separator => '|' }.merge(options)) - end - alias _ gettext - - def sgettext(msgid, separator = '|') - scope, msgid = I18n::Gettext.extract_scope(msgid, separator) - I18n.t(msgid, :scope => scope, :default => msgid) - end - alias s_ sgettext - - def pgettext(msgctxt, msgid) - separator = I18n::Gettext::CONTEXT_SEPARATOR - sgettext([msgctxt, msgid].join(separator), separator) - end - alias p_ pgettext - - def ngettext(msgid, msgid_plural, n = 1) - nsgettext(msgid, msgid_plural, n) - end - alias n_ ngettext - - # Method signatures: - # nsgettext('Fruits|apple', 'apples', 2) - # nsgettext(['Fruits|apple', 'apples'], 2) - def nsgettext(msgid, msgid_plural, n = 1, separator = '|') - if msgid.is_a?(Array) - msgid, msgid_plural, n, separator = msgid[0], msgid[1], msgid_plural, n - separator = '|' unless separator.is_a?(String) - end - - scope, msgid = I18n::Gettext.extract_scope(msgid, separator) - default = { :one => msgid, :other => msgid_plural } - I18n.t(msgid, :default => default, :count => n, :scope => scope, :separator => separator) - end - alias ns_ nsgettext - - # Method signatures: - # npgettext('Fruits', 'apple', 'apples', 2) - # npgettext('Fruits', ['apple', 'apples'], 2) - def npgettext(msgctxt, msgid, msgid_plural, n = 1) - separator = I18n::Gettext::CONTEXT_SEPARATOR - - if msgid.is_a?(Array) - msgid_plural, msgid, n = msgid[1], [msgctxt, msgid[0]].join(separator), msgid_plural - else - msgid = [msgctxt, msgid].join(separator) - end - - nsgettext(msgid, msgid_plural, n, separator) - end - alias np_ npgettext - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale.rb deleted file mode 100644 index 4f9d026..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale.rb +++ /dev/null @@ -1,6 +0,0 @@ -module I18n - module Locale - autoload :Fallbacks, 'i18n/locale/fallbacks' - autoload :Tag, 'i18n/locale/tag' - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/fallbacks.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/fallbacks.rb deleted file mode 100644 index 11dcf8c..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/fallbacks.rb +++ /dev/null @@ -1,98 +0,0 @@ -# encoding: utf-8 - -# Locale Fallbacks -# -# Extends the I18n module to hold a fallbacks instance which is set to an -# instance of I18n::Locale::Fallbacks by default but can be swapped with a -# different implementation. -# -# Locale fallbacks will compute a number of fallback locales for a given locale. -# For example: -# -#

-# I18n.fallbacks[:"es-MX"] # => [:"es-MX", :es, :en] 
-# -# Locale fallbacks always fall back to -# -# * all parent locales of a given locale (e.g. :es for :"es-MX") first, -# * the current default locales and all of their parents second -# -# The default locales are set to [I18n.default_locale] by default but can be -# set to something else. -# -# One can additionally add any number of additional fallback locales manually. -# These will be added before the default locales to the fallback chain. For -# example: -# -# # using the default locale as default fallback locale -# -# I18n.default_locale = :"en-US" -# I18n.fallbacks = I18n::Fallbacks.new(:"de-AT" => :"de-DE") -# I18n.fallbacks[:"de-AT"] # => [:"de-AT", :"de-DE", :de, :"en-US", :en] -# -# # using a custom locale as default fallback locale -# -# I18n.fallbacks = I18n::Fallbacks.new(:"en-GB", :"de-AT" => :de, :"de-CH" => :de) -# I18n.fallbacks[:"de-AT"] # => [:"de-AT", :de, :"en-GB", :en] -# I18n.fallbacks[:"de-CH"] # => [:"de-CH", :de, :"en-GB", :en] -# -# # mapping fallbacks to an existing instance -# -# # people speaking Catalan also speak Spanish as spoken in Spain -# fallbacks = I18n.fallbacks -# fallbacks.map(:ca => :"es-ES") -# fallbacks[:ca] # => [:ca, :"es-ES", :es, :"en-US", :en] -# -# # people speaking Arabian as spoken in Palestine also speak Hebrew as spoken in Israel -# fallbacks.map(:"ar-PS" => :"he-IL") -# fallbacks[:"ar-PS"] # => [:"ar-PS", :ar, :"he-IL", :he, :"en-US", :en] -# fallbacks[:"ar-EG"] # => [:"ar-EG", :ar, :"en-US", :en] -# -# # people speaking Sami as spoken in Finnland also speak Swedish and Finnish as spoken in Finnland -# fallbacks.map(:sms => [:"se-FI", :"fi-FI"]) -# fallbacks[:sms] # => [:sms, :"se-FI", :se, :"fi-FI", :fi, :"en-US", :en] - -module I18n - module Locale - class Fallbacks < Hash - def initialize(*mappings) - @map = {} - map(mappings.pop) if mappings.last.is_a?(Hash) - self.defaults = mappings.empty? ? [I18n.default_locale.to_sym] : mappings - end - - def defaults=(defaults) - @defaults = defaults.map { |default| compute(default, false) }.flatten - end - attr_reader :defaults - - def [](locale) - raise InvalidLocale.new(locale) if locale.nil? - locale = locale.to_sym - super || store(locale, compute(locale)) - end - - def map(mappings) - mappings.each do |from, to| - from, to = from.to_sym, Array(to) - to.each do |to| - @map[from] ||= [] - @map[from] << to.to_sym - end - end - end - - protected - - def compute(tags, include_defaults = true) - result = Array(tags).collect do |tag| - tags = I18n::Locale::Tag.tag(tag).self_and_parents.map! { |t| t.to_sym } - tags.each { |tag| tags += compute(@map[tag]) if @map[tag] } - tags - end.flatten - result.push(*defaults) if include_defaults - result.uniq - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag.rb deleted file mode 100644 index a640b44..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag.rb +++ /dev/null @@ -1,28 +0,0 @@ -# encoding: utf-8 - -module I18n - module Locale - module Tag - autoload :Parents, 'i18n/locale/tag/parents' - autoload :Rfc4646, 'i18n/locale/tag/rfc4646' - autoload :Simple, 'i18n/locale/tag/simple' - - class << self - # Returns the current locale tag implementation. Defaults to +I18n::Locale::Tag::Simple+. - def implementation - @@implementation ||= Simple - end - - # Sets the current locale tag implementation. Use this to set a different locale tag implementation. - def implementation=(implementation) - @@implementation = implementation - end - - # Factory method for locale tags. Delegates to the current locale tag implementation. - def tag(tag) - implementation.tag(tag) - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/parents.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/parents.rb deleted file mode 100644 index a094468..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/parents.rb +++ /dev/null @@ -1,24 +0,0 @@ -# encoding: utf-8 - -module I18n - module Locale - module Tag - module Parents - def parent - @parent ||= begin - segs = to_a.compact - segs.length > 1 ? self.class.tag(*segs[0..(segs.length-2)].join('-')) : nil - end - end - - def self_and_parents - @self_and_parents ||= [self] + parents - end - - def parents - @parents ||= ([parent] + (parent ? parent.parents : [])).compact - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/rfc4646.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/rfc4646.rb deleted file mode 100644 index c20d35a..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/rfc4646.rb +++ /dev/null @@ -1,76 +0,0 @@ -# encoding: utf-8 - -# RFC 4646/47 compliant Locale tag implementation that parses locale tags to -# subtags such as language, script, region, variant etc. -# -# For more information see by http://en.wikipedia.org/wiki/IETF_language_tag -# -# Rfc4646::Parser does not implement grandfathered tags. - -module I18n - module Locale - module Tag - RFC4646_SUBTAGS = [ :language, :script, :region, :variant, :extension, :privateuse, :grandfathered ] - RFC4646_FORMATS = { :language => :downcase, :script => :capitalize, :region => :upcase, :variant => :downcase } - - class Rfc4646 < Struct.new(*RFC4646_SUBTAGS) - class << self - # Parses the given tag and returns a Tag instance if it is valid. - # Returns false if the given tag is not valid according to RFC 4646. - def tag(tag) - matches = parser.match(tag) - new(*matches) if matches - end - - def parser - @@parser ||= Rfc4646::Parser - end - - def parser=(parser) - @@parser = parser - end - end - - include Parents - - RFC4646_FORMATS.each do |name, format| - define_method(name) { self[name].send(format) unless self[name].nil? } - end - - def to_sym - to_s.to_sym - end - - def to_s - @tag ||= to_a.compact.join("-") - end - - def to_a - members.collect { |attr| self.send(attr) } - end - - module Parser - PATTERN = %r{\A(?: - ([a-z]{2,3}(?:(?:-[a-z]{3}){0,3})?|[a-z]{4}|[a-z]{5,8}) # language - (?:-([a-z]{4}))? # script - (?:-([a-z]{2}|\d{3}))? # region - (?:-([0-9a-z]{5,8}|\d[0-9a-z]{3}))* # variant - (?:-([0-9a-wyz](?:-[0-9a-z]{2,8})+))* # extension - (?:-(x(?:-[0-9a-z]{1,8})+))?| # privateuse subtag - (x(?:-[0-9a-z]{1,8})+)| # privateuse tag - /* ([a-z]{1,3}(?:-[0-9a-z]{2,8}){1,2}) */ # grandfathered - )\z}xi - - class << self - def match(tag) - c = PATTERN.match(tag.to_s).captures - c[0..4] << (c[5].nil? ? c[6] : c[5]) << c[7] # TODO c[7] is grandfathered, throw a NotImplemented exception here? - rescue - false - end - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/simple.rb deleted file mode 100644 index 0fddb36..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/locale/tag/simple.rb +++ /dev/null @@ -1,41 +0,0 @@ -# encoding: utf-8 - -# Simple Locale tag implementation that computes subtags by simply splitting -# the locale tag at '-' occurences. -module I18n - module Locale - module Tag - class Simple - class << self - def tag(tag) - new(tag) - end - end - - include Parents - - attr_reader :tag - - def initialize(*tag) - @tag = tag.join('-').to_sym - end - - def subtags - @subtags = tag.to_s.split('-').map { |subtag| subtag.to_s } - end - - def to_sym - tag - end - - def to_s - tag.to_s - end - - def to_a - subtags - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/version.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/version.rb deleted file mode 100644 index 6a59d2b..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/lib/i18n/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -module I18n - VERSION = "0.3.3" -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/all.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/all.rb deleted file mode 100644 index e846143..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/all.rb +++ /dev/null @@ -1,8 +0,0 @@ -# encoding: utf-8 - -dir = File.dirname(__FILE__) -$LOAD_PATH.unshift(dir) - -Dir["#{dir}/**/*_test.rb"].sort.each do |file| - require file.sub(/^#{dir}\/(.*)\.rb$/, '\1') -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/basics.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/basics.rb deleted file mode 100644 index 4eeb363..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/basics.rb +++ /dev/null @@ -1,15 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Basics - def test_available_locales - store_translations('de', :foo => 'bar') - store_translations('en', :foo => 'foo') - - assert I18n.available_locales.include?(:de) - assert I18n.available_locales.include?(:en) - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/defaults.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/defaults.rb deleted file mode 100644 index 62462f3..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/defaults.rb +++ /dev/null @@ -1,40 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Defaults - def setup - super - store_translations(:foo => { :bar => 'bar', :baz => 'baz' }) - end - - define_method "test defaults: given nil as a key it returns the given default" do - assert_equal 'default', I18n.t(nil, :default => 'default') - end - - define_method "test defaults: given a symbol as a default it translates the symbol" do - assert_equal 'bar', I18n.t(nil, :default => :'foo.bar') - end - - define_method "test defaults: given a symbol as a default and a scope it stays inside the scope when looking up the symbol" do - assert_equal 'bar', I18n.t(:missing, :default => :bar, :scope => :foo) - end - - define_method "test defaults: given an array as a default it returns the first match" do - assert_equal 'bar', I18n.t(:does_not_exist, :default => [:does_not_exist_2, :'foo.bar']) - end - - define_method "test defaults: given an array of missing keys it raises a MissingTranslationData exception" do - assert_raises I18n::MissingTranslationData do - I18n.t(:does_not_exist, :default => [:does_not_exist_2, :does_not_exist_3], :raise => true) - end - end - - define_method "test defaults: using a custom scope separator" do - # data must have been stored using the custom separator when using the ActiveRecord backend - I18n.backend.store_translations(:en, { :foo => { :bar => 'bar' } }, { :separator => '|' }) - assert_equal 'bar', I18n.t(nil, :default => :'foo|bar', :separator => '|') - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/interpolation.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/interpolation.rb deleted file mode 100644 index 576d2ea..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/interpolation.rb +++ /dev/null @@ -1,92 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Interpolation - def interpolate(*args) - options = args.last.is_a?(Hash) ? args.pop : {} - key = args.pop - I18n.backend.translate('en', key, options) - end - - define_method "test interpolation: given no values it does not alter the string" do - assert_equal 'Hi {{name}}!', interpolate(:default => 'Hi {{name}}!') - end - - define_method "test interpolation: given values it interpolates them into the string" do - assert_equal 'Hi David!', interpolate(:default => 'Hi {{name}}!', :name => 'David') - end - - define_method "test interpolation: given a nil value it still interpolates it into the string" do - assert_equal 'Hi !', interpolate(:default => 'Hi {{name}}!', :name => nil) - end - - define_method "test interpolation: given a lambda as a value it calls it if the string contains the key" do - assert_equal 'Hi David!', interpolate(:default => 'Hi {{name}}!', :name => lambda { |*args| 'David' }) - end - - define_method "test interpolation: given a lambda as a value it does not call it if the string does not contain the key" do - assert_nothing_raised { interpolate(:default => 'Hi!', :name => lambda { |*args| raise 'fail' }) } - end - - define_method "test interpolation: given values but missing a key it raises I18n::MissingInterpolationArgument" do - assert_raises(I18n::MissingInterpolationArgument) do - interpolate(:default => '{{foo}}', :bar => 'bar') - end - end - - define_method "test interpolation: it does not raise I18n::MissingInterpolationArgument for escaped variables" do - assert_nothing_raised(I18n::MissingInterpolationArgument) do - assert_equal 'Barr {{foo}}', interpolate(:default => '{{bar}} \{{foo}}', :bar => 'Barr') - end - end - - define_method "test interpolation: it does not change the original, stored translation string" do - I18n.backend.store_translations(:en, :interpolate => 'Hi {{name}}!') - assert_equal 'Hi David!', interpolate(:interpolate, :name => 'David') - assert_equal 'Hi Yehuda!', interpolate(:interpolate, :name => 'Yehuda') - end - - define_method "test interpolation: works with Ruby 1.9 syntax" do - assert_equal 'Hi David!', interpolate(:default => 'Hi %{name}!', :name => 'David') - end - - define_method "test interpolation: given the translation is in utf-8 it still works" do - assert_equal 'Häi David!', interpolate(:default => 'Häi {{name}}!', :name => 'David') - end - - define_method "test interpolation: given the value is in utf-8 it still works" do - assert_equal 'Hi ゆきひろ!', interpolate(:default => 'Hi {{name}}!', :name => 'ゆきひろ') - end - - define_method "test interpolation: given the translation and the value are in utf-8 it still works" do - assert_equal 'こんにちは、ゆきひろさん!', interpolate(:default => 'こんにちは、{{name}}さん!', :name => 'ゆきひろ') - end - - if Kernel.const_defined?(:Encoding) - define_method "test interpolation: given a euc-jp translation and a utf-8 value it raises Encoding::CompatibilityError" do - assert_raises(Encoding::CompatibilityError) do - interpolate(:default => euc_jp('こんにちは、{{name}}さん!'), :name => 'ゆきひろ') - end - end - - # define_method "test interpolation: given a utf-8 translation and a euc-jp value it returns a translation in euc-jp" do - # assert_equal euc_jp('Hi ゆきひろ!'), interpolate(:default => 'Hi {{name}}!', :name => euc_jp('ゆきひろ')) - # end - # - # TODO should better explain how this relates to the test above with the simpler utf-8 default string - define_method "test interpolation: given a utf-8 translation and a euc-jp value it raises Encoding::CompatibilityError" do - assert_raises(Encoding::CompatibilityError) do - interpolate(:default => 'こんにちは、{{name}}さん!', :name => euc_jp('ゆきひろ')) - end - end - end - - define_method "test interpolation: given a translations containing a reserved key it raises I18n::ReservedInterpolationKey" do - assert_raises(I18n::ReservedInterpolationKey) { interpolate(:default => '{{default}}', :foo => :bar) } - assert_raises(I18n::ReservedInterpolationKey) { interpolate(:default => '{{scope}}', :foo => :bar) } - assert_raises(I18n::ReservedInterpolationKey) { interpolate(:default => '{{separator}}', :foo => :bar) } - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/link.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/link.rb deleted file mode 100644 index 534e650..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/link.rb +++ /dev/null @@ -1,55 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Link - define_method "test linked lookup: if a key resolves to a symbol it looks up the symbol" do - I18n.backend.store_translations 'en', { - :link => :linked, - :linked => 'linked' - } - assert_equal 'linked', I18n.backend.translate('en', :link) - end - - define_method "test linked lookup: if a key resolves to a dot-separated symbol it looks up the symbol" do - I18n.backend.store_translations 'en', { - :link => :"foo.linked", - :foo => { :linked => 'linked' } - } - assert_equal('linked', I18n.backend.translate('en', :link)) - end - - define_method "test linked lookup: if a dot-separated key resolves to a symbol it looks up the symbol" do - I18n.backend.store_translations 'en', { - :foo => { :link => :linked }, - :linked => 'linked' - } - assert_equal('linked', I18n.backend.translate('en', :'foo.link')) - end - - define_method "test linked lookup: if a dot-separated key resolves to a dot-separated symbol it looks up the symbol" do - I18n.backend.store_translations 'en', { - :foo => { :link => :"bar.linked" }, - :bar => { :linked => 'linked' } - } - assert_equal('linked', I18n.backend.translate('en', :'foo.link')) - end - - define_method "test linked lookup: links refer to absolute keys even if a scope was given" do - I18n.backend.store_translations 'en', { - :foo => { :link => :linked, :linked => 'linked in foo' }, - :linked => 'linked absolutely' - } - assert_equal 'linked absolutely', I18n.backend.translate('en', :link, :scope => :foo) - end - - define_method "test linked lookup: a link can resolve to a namespace in the middle of a dot-separated key" do - I18n.backend.store_translations 'en', { - :activemodel => { :errors => { :messages => { :blank => "can't be blank" } } }, - :activerecord => { :errors => { :messages => :"activemodel.errors.messages" } } - } - assert_equal "can't be blank", I18n.t(:"activerecord.errors.messages.blank") - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/date.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/date.rb deleted file mode 100644 index 9454c7f..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/date.rb +++ /dev/null @@ -1,91 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Localization - module Date - def setup - super - setup_date_translations - @date = ::Date.new(2008, 3, 1) - end - - define_method "test localize Date: given the short format it uses it" do - # TODO should be Mrz, shouldn't it? - assert_equal '01. Mar', I18n.l(@date, :format => :short, :locale => :de) - end - - define_method "test localize Date: given the long format it uses it" do - assert_equal '01. März 2008', I18n.l(@date, :format => :long, :locale => :de) - end - - define_method "test localize Date: given the default format it uses it" do - assert_equal '01.03.2008', I18n.l(@date, :format => :default, :locale => :de) - end - - define_method "test localize Date: given a day name format it returns the correct day name" do - assert_equal 'Samstag', I18n.l(@date, :format => '%A', :locale => :de) - end - - define_method "test localize Date: given an abbreviated day name format it returns the correct abbreviated day name" do - assert_equal 'Sa', I18n.l(@date, :format => '%a', :locale => :de) - end - - define_method "test localize Date: given a month name format it returns the correct month name" do - assert_equal 'März', I18n.l(@date, :format => '%B', :locale => :de) - end - - define_method "test localize Date: given an abbreviated month name format it returns the correct abbreviated month name" do - # TODO should be Mrz, shouldn't it? - assert_equal 'Mar', I18n.l(@date, :format => '%b', :locale => :de) - end - - define_method "test localize Date: given a format that resolves to a Proc it calls the Proc with the object" do - # TODO should be Mrz, shouldn't it? - assert_equal '[Sat, 01 Mar 2008, {}]', I18n.l(@date, :format => :proc, :locale => :de) - end - - # TODO fails, but something along these lines probably should pass - # define_method "test localize Date: given a format that resolves to a Proc it calls the Proc with the object and extra options" do - # assert_equal '[Sat Mar 01 06:00:00 UTC 2008, {:foo=>"foo"}]', I18n.l(@time, :format => :proc, :foo => 'foo', :locale => :de) - # end - - define_method "test localize Date: given an unknown format it does not fail" do - assert_nothing_raised { I18n.l(@date, :format => '%x') } - end - - define_method "test localize Date: given nil it raises I18n::ArgumentError" do - assert_raises(I18n::ArgumentError) { I18n.l(nil) } - end - - define_method "test localize Date: given a plain Object it raises I18n::ArgumentError" do - assert_raises(I18n::ArgumentError) { I18n.l(Object.new) } - end - - define_method "test localize Date: it does not alter the format string" do - assert_equal '01. Februar 2009', I18n.l(::Date.parse('2009-02-01'), :format => :long, :locale => :de) - assert_equal '01. Oktober 2009', I18n.l(::Date.parse('2009-10-01'), :format => :long, :locale => :de) - end - - protected - - def setup_date_translations - store_translations :de, { - :date => { - :formats => { - :default => "%d.%m.%Y", - :short => "%d. %b", - :long => "%d. %B %Y", - :proc => lambda { |*args| args.inspect } - }, - :day_names => %w(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag), - :abbr_day_names => %w(So Mo Di Mi Do Fr Sa), - :month_names => %w(Januar Februar März April Mai Juni Juli August September Oktober November Dezember).unshift(nil), - :abbr_month_names => %w(Jan Feb Mar Apr Mai Jun Jul Aug Sep Okt Nov Dez).unshift(nil) - } - } - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/date_time.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/date_time.rb deleted file mode 100644 index 7225694..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/date_time.rb +++ /dev/null @@ -1,90 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Localization - module DateTime - def setup - super - setup_datetime_translations - @datetime = ::DateTime.new(2008, 3, 1, 6) - @other_datetime = ::DateTime.new(2008, 3, 1, 18) - end - - define_method "test localize DateTime: given the short format it uses it" do - # TODO should be Mrz, shouldn't it? - assert_equal '01. Mar 06:00', I18n.l(@datetime, :format => :short, :locale => :de) - end - - define_method "test localize DateTime: given the long format it uses it" do - assert_equal '01. März 2008 06:00', I18n.l(@datetime, :format => :long, :locale => :de) - end - - define_method "test localize DateTime: given the default format it uses it" do - # TODO should be Mrz, shouldn't it? - assert_equal 'Sa, 01. Mar 2008 06:00:00 +0000', I18n.l(@datetime, :format => :default, :locale => :de) - end - - define_method "test localize DateTime: given a day name format it returns the correct day name" do - assert_equal 'Samstag', I18n.l(@datetime, :format => '%A', :locale => :de) - end - - define_method "test localize DateTime: given an abbreviated day name format it returns the correct abbreviated day name" do - assert_equal 'Sa', I18n.l(@datetime, :format => '%a', :locale => :de) - end - - define_method "test localize DateTime: given a month name format it returns the correct month name" do - assert_equal 'März', I18n.l(@datetime, :format => '%B', :locale => :de) - end - - define_method "test localize DateTime: given an abbreviated month name format it returns the correct abbreviated month name" do - # TODO should be Mrz, shouldn't it? - assert_equal 'Mar', I18n.l(@datetime, :format => '%b', :locale => :de) - end - - define_method "test localize DateTime: given a meridian indicator format it returns the correct meridian indicator" do - assert_equal 'am', I18n.l(@datetime, :format => '%p', :locale => :de) - assert_equal 'pm', I18n.l(@other_datetime, :format => '%p', :locale => :de) - end - - define_method "test localize Date: given a format that resolves to a Proc it calls the Proc with the object" do - if can_store_procs? - assert_equal '[Sat, 01 Mar 2008 06:00:00 +0000, {}]', I18n.l(@datetime, :format => :proc, :locale => :de) - end - end - - # TODO fails, but something along these lines probably should pass - # define_method "test localize DateTime: given a format that resolves to a Proc it calls the Proc with the object and extra options" do - # assert_equal '1ter März 2008, 06:00 Uhr', I18n.l(@datetime, :long_ordinalized) - # end - - define_method "test localize DateTime: given an unknown format it does not fail" do - assert_nothing_raised { I18n.l(@datetime, :format => '%x') } - end - - protected - - def setup_datetime_translations - # time translations might have been set up in Tests::Api::Localization::Time - I18n.t(:'time.formats.default', :locale => :de, :raise => true) - rescue - store_translations :de, { - :time => { - :formats => { - :default => "%a, %d. %b %Y %H:%M:%S %z", - :short => "%d. %b %H:%M", - :long => "%d. %B %Y %H:%M", - :long_ordinalized => lambda { |date, options| - tz = " (#{options[:timezone]})" if options[:timezone] - "#{date.day}ter %B %Y, %H:%M Uhr#{tz}" - } - }, - :am => 'am', - :pm => 'pm' - } - } - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/procs.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/procs.rb deleted file mode 100644 index d7c5fd4..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/procs.rb +++ /dev/null @@ -1,54 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Localization - module Procs - define_method "test localize: using day names from lambdas" do - setup_time_proc_translations - time = ::Time.parse('2008-03-01 6:00 UTC') - assert_match /Суббота/, I18n.l(time, :format => "%A, %d %B", :locale => :ru) - assert_match /суббота/, I18n.l(time, :format => "%d %B (%A)", :locale => :ru) - end - - define_method "test localize: using month names from lambdas" do - setup_time_proc_translations - time = ::Time.parse('2008-03-01 6:00 UTC') - assert_match /марта/, I18n.l(time, :format => "%d %B %Y", :locale => :ru) - assert_match /Март /, I18n.l(time, :format => "%B %Y", :locale => :ru) - end - - define_method "test localize: using abbreviated day names from lambdas" do - setup_time_proc_translations - time = ::Time.parse('2008-03-01 6:00 UTC') - assert_match /марта/, I18n.l(time, :format => "%d %b %Y", :locale => :ru) - assert_match /март /, I18n.l(time, :format => "%b %Y", :locale => :ru) - end - - protected - - def setup_time_proc_translations - store_translations :ru, { - :date => { - :'day_names' => lambda { |key, options| - (options[:format] =~ /^%A/) ? - %w(Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота) : - %w(воскресенье понедельник вторник среда четверг пятница суббота) - }, - :'month_names' => lambda { |key, options| - (options[:format] =~ /(%d|%e)(\s*)?(%B)/) ? - %w(января февраля марта апреля мая июня июля августа сентября октября ноября декабря).unshift(nil) : - %w(Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь).unshift(nil) - }, - :'abbr_month_names' => lambda { |key, options| - (options[:format] =~ /(%d|%e)(\s*)(%b)/) ? - %w(янв. февр. марта апр. мая июня июля авг. сент. окт. нояб. дек.).unshift(nil) : - %w(янв. февр. март апр. май июнь июль авг. сент. окт. нояб. дек.).unshift(nil) - }, - } - } - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/time.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/time.rb deleted file mode 100644 index eca2fae..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/localization/time.rb +++ /dev/null @@ -1,84 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Localization - module Time - def setup - super - setup_time_translations - @time = ::Time.parse('2008-03-01 6:00 UTC') - @other_time = ::Time.parse('2008-03-01 18:00 UTC') - end - - define_method "test localize Time: given the short format it uses it" do - # TODO should be Mrz, shouldn't it? - assert_equal '01. Mar 06:00', I18n.l(@time, :format => :short, :locale => :de) - end - - define_method "test localize Time: given the long format it uses it" do - assert_equal '01. März 2008 06:00', I18n.l(@time, :format => :long, :locale => :de) - end - - # TODO Seems to break on Windows because ENV['TZ'] is ignored. What's a better way to do this? - # def test_localize_given_the_default_format_it_uses_it - # assert_equal 'Sa, 01. Mar 2008 06:00:00 +0000', I18n.l(@time, :format => :default, :locale => :de) - # end - - define_method "test localize Time: given a day name format it returns the correct day name" do - assert_equal 'Samstag', I18n.l(@time, :format => '%A', :locale => :de) - end - - define_method "test localize Time: given an abbreviated day name format it returns the correct abbreviated day name" do - assert_equal 'Sa', I18n.l(@time, :format => '%a', :locale => :de) - end - - define_method "test localize Time: given a month name format it returns the correct month name" do - assert_equal 'März', I18n.l(@time, :format => '%B', :locale => :de) - end - - define_method "test localize Time: given an abbreviated month name format it returns the correct abbreviated month name" do - # TODO should be Mrz, shouldn't it? - assert_equal 'Mar', I18n.l(@time, :format => '%b', :locale => :de) - end - - define_method "test localize Time: given a meridian indicator format it returns the correct meridian indicator" do - assert_equal 'am', I18n.l(@time, :format => '%p', :locale => :de) - assert_equal 'pm', I18n.l(@other_time, :format => '%p', :locale => :de) - end - - define_method "test localize Time: given a format that resolves to a Proc it calls the Proc with the object" do - if can_store_procs? - assert_equal '[Sat, 01 Mar 2008 06:00:00 +0000, {}]', I18n.l(@datetime, :format => :proc, :locale => :de) - end - end - - # TODO fails, but something along these lines probably should pass - # define_method "test localize Time: given a format that resolves to a Proc it calls the Proc with the object and extra options" do - # assert_equal '[Sat Mar 01 06:00:00 UTC 2008, {:foo=>"foo"}]', I18n.l(@time, :format => :proc, :foo => 'foo', :locale => :de) - # end - - define_method "test localize Time: given an unknown format it does not fail" do - assert_nothing_raised { I18n.l(@time, :format => '%x') } - end - - protected - - def setup_time_translations - store_translations :de, { - :time => { - :formats => { - :default => "%a, %d. %b %Y %H:%M:%S %z", - :short => "%d. %b %H:%M", - :long => "%d. %B %Y %H:%M", - :proc => lambda { |*args| args.inspect } - }, - :am => 'am', - :pm => 'pm' - } - } - end - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/lookup.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/lookup.rb deleted file mode 100644 index ce7b488..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/lookup.rb +++ /dev/null @@ -1,45 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Lookup - def setup - super - store_translations(:foo => { :bar => 'bar', :baz => 'baz' }, :bla => false) - end - - define_method "test lookup: given a nested key it looks up the nested hash value" do - assert_equal 'bar', I18n.t(:'foo.bar') - end - - define_method "test make sure we can store a native false value as well" do - assert_equal false, I18n.t(:bla) - end - - define_method "test lookup: given a missing key, no default and no raise option it returns an error message" do - assert_equal "translation missing: en, missing", I18n.t(:missing) - end - - define_method "test lookup: given a missing key, no default and the raise option it raises MissingTranslationData" do - assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) } - end - - define_method "test lookup: given an array of keys it translates all of them" do - assert_equal %w(bar baz), I18n.t([:bar, :baz], :scope => [:foo]) - end - - define_method "test lookup: using a custom scope separator" do - # data must have been stored using the custom separator when using the ActiveRecord backend - I18n.backend.store_translations(:en, { :foo => { :bar => 'bar' } }, { :separator => '|' }) - assert_equal 'bar', I18n.t('foo|bar', :separator => '|') - end - - # In fact it probably *should* fail but Rails currently relies on using the default locale instead. - # So we'll stick to this for now until we get it fixed in Rails. - define_method "test lookup: given nil as a locale it does not raise but use the default locale" do - # assert_raises(I18n::InvalidLocale) { I18n.t(:bar, :locale => nil) } - assert_nothing_raised { I18n.t(:bar, :locale => nil) } - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/pluralization.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/pluralization.rb deleted file mode 100644 index 36413a2..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/pluralization.rb +++ /dev/null @@ -1,35 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Pluralization - define_method "test pluralization: given 0 it returns the :zero translation if it is defined" do - assert_equal 'zero', I18n.t(:default => { :zero => 'zero' }, :count => 0) - end - - define_method "test pluralization: given 0 it returns the :other translation if :zero is not defined" do - assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 0) - end - - define_method "test pluralization: given 1 it returns the singular translation" do - assert_equal 'bar', I18n.t(:default => { :one => 'bar' }, :count => 1) - end - - define_method "test pluralization: given 2 it returns the :other translation" do - assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 2) - end - - define_method "test pluralization: given 3 it returns the :other translation" do - assert_equal 'bars', I18n.t(:default => { :other => 'bars' }, :count => 3) - end - - define_method "test pluralization: given nil it returns the whole entry" do - assert_equal({ :one => 'bar' }, I18n.t(:default => { :one => 'bar' }, :count => nil)) - end - - define_method "test pluralization: given incomplete pluralization data it raises I18n::InvalidPluralizationData" do - assert_raises(I18n::InvalidPluralizationData) { I18n.t(:default => { :one => 'bar' }, :count => 2) } - end - end - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/procs.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/procs.rb deleted file mode 100644 index 952759d..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/api/procs.rb +++ /dev/null @@ -1,40 +0,0 @@ -# encoding: utf-8 - -module Tests - module Api - module Procs - define_method "test lookup: given a translation is a proc it calls the proc with the key and interpolation values" do - store_translations(:a_lambda => lambda { |*args| args.inspect }) - assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(:a_lambda, :foo => 'foo') - end - - define_method "test defaults: given a default is a Proc it calls it with the key and interpolation values" do - proc = lambda { |*args| args.inspect } - assert_equal '[nil, {:foo=>"foo"}]', I18n.t(nil, :default => proc, :foo => 'foo') - end - - define_method "test defaults: given a default is a key that resolves to a Proc it calls it with the key and interpolation values" do - store_translations(:a_lambda => lambda { |*args| args.inspect }) - assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(nil, :default => :a_lambda, :foo => 'foo') - assert_equal '[:a_lambda, {:foo=>"foo"}]', I18n.t(nil, :default => [nil, :a_lambda], :foo => 'foo') - end - - define_method "test interpolation: given an interpolation value is a lambda it calls it with key and values before interpolating it" do - proc = lambda { |*args| args.inspect } - assert_match %r(\[\{:foo=>#\}\]), I18n.t(nil, :default => '{{foo}}', :foo => proc) - end - - define_method "test interpolation: given a key resolves to a Proc that returns a string then interpolation still works" do - proc = lambda { |*args| "{{foo}}: " + args.inspect } - assert_equal 'foo: [nil, {:foo=>"foo"}]', I18n.t(nil, :default => proc, :foo => 'foo') - end - - define_method "test pluralization: given a key resolves to a Proc that returns valid data then pluralization still works" do - proc = lambda { |*args| { :zero => 'zero', :one => 'one', :other => 'other' } } - assert_equal 'zero', I18n.t(:default => proc, :count => 0) - assert_equal 'one', I18n.t(:default => proc, :count => 1) - assert_equal 'other', I18n.t(:default => proc, :count => 2) - end - end - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/active_record_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/active_record_test.rb deleted file mode 100644 index f886e6d..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/active_record_test.rb +++ /dev/null @@ -1,29 +0,0 @@ -# encoding: utf-8 -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -setup_active_record - - -class I18nActiveRecordApiTest < Test::Unit::TestCase - def setup - I18n.backend = I18n::Backend::ActiveRecord.new - super - end - - include Tests::Api::Basics - include Tests::Api::Defaults - include Tests::Api::Interpolation - include Tests::Api::Link - include Tests::Api::Lookup - include Tests::Api::Pluralization - include Tests::Api::Procs unless RUBY_VERSION >= '1.9.1' - - include Tests::Api::Localization::Date - include Tests::Api::Localization::DateTime - include Tests::Api::Localization::Time - include Tests::Api::Localization::Procs unless RUBY_VERSION >= '1.9.1' - - define_method "test: make sure we use an ActiveRecord backend" do - assert_equal I18n::Backend::ActiveRecord, I18n.backend.class - end -end if defined?(ActiveRecord) diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/all_features_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/all_features_test.rb deleted file mode 100644 index 6474ec5..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/all_features_test.rb +++ /dev/null @@ -1,40 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nAllFeaturesApiTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::Cache - include I18n::Backend::Metadata - include I18n::Backend::Cascade - include I18n::Backend::Fallbacks - include I18n::Backend::Pluralization - include I18n::Backend::Fast - include I18n::Backend::InterpolationCompiler - end - - def setup - I18n.backend = I18n::Backend::Chain.new(Backend.new, I18n::Backend::Simple.new) - super - end - - include Tests::Api::Basics - include Tests::Api::Defaults - include Tests::Api::Interpolation - include Tests::Api::Link - include Tests::Api::Lookup - include Tests::Api::Pluralization - include Tests::Api::Procs - include Tests::Api::Localization::Date - include Tests::Api::Localization::DateTime - include Tests::Api::Localization::Time - include Tests::Api::Localization::Procs - - define_method "test: make sure we use a Chain backend with an all features backend" do - assert_equal I18n::Backend::Chain, I18n.backend.class - assert_equal Backend, I18n.backend.backends.first.class - end - - # links: test that keys stored on one backend can link to keys stored on another backend -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/cascade_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/cascade_test.rb deleted file mode 100644 index ad33999..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/cascade_test.rb +++ /dev/null @@ -1,31 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nCascadeApiTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::Cascade - end - - def setup - I18n.backend = Backend.new - super - end - - include Tests::Api::Basics - include Tests::Api::Defaults - include Tests::Api::Interpolation - include Tests::Api::Link - include Tests::Api::Lookup - include Tests::Api::Pluralization - include Tests::Api::Procs - include Tests::Api::Localization::Date - include Tests::Api::Localization::DateTime - include Tests::Api::Localization::Time - include Tests::Api::Localization::Procs - - define_method "test: make sure we use a backend with Cascade included" do - assert_equal Backend, I18n.backend.class - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/chain_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/chain_test.rb deleted file mode 100644 index 5b92c5d..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/chain_test.rb +++ /dev/null @@ -1,26 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nApiChainTest < Test::Unit::TestCase - def setup - super - I18n.backend = I18n::Backend::Chain.new(I18n::Backend::Simple.new, I18n.backend) - end - - include Tests::Api::Basics - include Tests::Api::Defaults - include Tests::Api::Interpolation - include Tests::Api::Link - include Tests::Api::Lookup - include Tests::Api::Pluralization - include Tests::Api::Procs - include Tests::Api::Localization::Date - include Tests::Api::Localization::DateTime - include Tests::Api::Localization::Time - include Tests::Api::Localization::Procs - - define_method "test: make sure we use the Chain backend" do - assert_equal I18n::Backend::Chain, I18n.backend.class - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/fallbacks_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/fallbacks_test.rb deleted file mode 100644 index 406e5bc..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/fallbacks_test.rb +++ /dev/null @@ -1,33 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nFallbacksApiTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::Fallbacks - end - - def setup - I18n.backend = Backend.new - super - end - - include Tests::Api::Basics - include Tests::Api::Defaults - include Tests::Api::Interpolation - include Tests::Api::Link - include Tests::Api::Lookup - include Tests::Api::Pluralization - include Tests::Api::Procs - include Tests::Api::Localization::Date - include Tests::Api::Localization::DateTime - include Tests::Api::Localization::Time - include Tests::Api::Localization::Procs - - define_method "test: make sure we use a backend with Fallbacks included" do - assert_equal Backend, I18n.backend.class - end - - # links: test that keys stored on one backend can link to keys stored on another backend -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/fast_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/fast_test.rb deleted file mode 100644 index 9cc7794..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/fast_test.rb +++ /dev/null @@ -1,31 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nFastBackendApiTest < Test::Unit::TestCase - include Tests::Api::Basics - include Tests::Api::Defaults - include Tests::Api::Interpolation - include Tests::Api::Link - include Tests::Api::Lookup - include Tests::Api::Pluralization - include Tests::Api::Procs - include Tests::Api::Localization::Date - include Tests::Api::Localization::DateTime - include Tests::Api::Localization::Time - include Tests::Api::Localization::Procs - - class FastBackend - include I18n::Backend::Base - include I18n::Backend::Fast - end - - def setup - I18n.backend = FastBackend.new - super - end - - define_method "test: make sure we use the FastBackend backend" do - assert_equal FastBackend, I18n.backend.class - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/pluralization_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/pluralization_test.rb deleted file mode 100644 index 1d95e24..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/pluralization_test.rb +++ /dev/null @@ -1,33 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nPluralizationApiTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::Pluralization - end - - def setup - I18n.backend = Backend.new - super - end - - include Tests::Api::Basics - include Tests::Api::Defaults - include Tests::Api::Interpolation - include Tests::Api::Link - include Tests::Api::Lookup - include Tests::Api::Pluralization - include Tests::Api::Procs - include Tests::Api::Localization::Date - include Tests::Api::Localization::DateTime - include Tests::Api::Localization::Time - include Tests::Api::Localization::Procs - - define_method "test: make sure we use a backend with Pluralization included" do - assert_equal Backend, I18n.backend.class - end - - # links: test that keys stored on one backend can link to keys stored on another backend -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/simple_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/simple_test.rb deleted file mode 100644 index cd4b7a5..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/api/simple_test.rb +++ /dev/null @@ -1,21 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nSimpleBackendApiTest < Test::Unit::TestCase - include Tests::Api::Basics - include Tests::Api::Defaults - include Tests::Api::Interpolation - include Tests::Api::Link - include Tests::Api::Lookup - include Tests::Api::Pluralization - include Tests::Api::Procs - include Tests::Api::Localization::Date - include Tests::Api::Localization::DateTime - include Tests::Api::Localization::Time - include Tests::Api::Localization::Procs - - define_method "test: make sure we use the Simple backend" do - assert_equal I18n::Backend::Simple, I18n.backend.class - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/active_record/missing_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/active_record/missing_test.rb deleted file mode 100644 index 395ae3c..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/active_record/missing_test.rb +++ /dev/null @@ -1,60 +0,0 @@ -# encoding: utf-8 -require File.expand_path(File.dirname(__FILE__) + '/../../../test_helper') - -setup_active_record - -class I18nActiveRecordMissingTest < Test::Unit::TestCase - def setup - store_translations(:en, :i18n => { :plural => { :keys => [:zero, :one, :other] } }) - - I18n.backend = I18n::Backend::Chain.new(I18n.backend) - I18n.backend.meta_class.send(:include, I18n::Backend::ActiveRecord::Missing) - - I18n::Backend::ActiveRecord::Translation.delete_all - end - - def test_can_persist_interpolations - translation = I18n::Backend::ActiveRecord::Translation.new \ - :key => 'foo', - :value => 'bar', - :locale => :en - - translation.interpolations = %w{ count name } - translation.save - - assert translation.valid? - end - - def test_lookup_persists_key - I18n.t('foo.bar.baz') - - assert_equal 1, I18n::Backend::ActiveRecord::Translation.count - end - - def test_lookup_does_not_persist_key_twice - 2.times { I18n.t('foo.bar.baz') } - - assert_equal 1, I18n::Backend::ActiveRecord::Translation.count - end - - def test_persists_interpolation_keys_when_looked_up_directly - I18n.t('foo.bar.baz', :cow => "lucy" ) # creates stub translation. - - translation_stub = I18n::Backend::ActiveRecord::Translation.locale(:en).lookup('foo.bar.baz').first - assert translation_stub.interpolates?(:cow) - end - - def test_creates_one_stub_per_pluralization - I18n.t('foo', :count => 999) - - translations = I18n::Backend::ActiveRecord::Translation.locale(:en).find_all_by_key %w{ foo.zero foo.one foo.other } - assert_equal 3, translations.length - end - - def test_creates_no_stub_for_base_key_in_pluralization - I18n.t('foo', :count => 999) - - translations = I18n::Backend::ActiveRecord::Translation.locale(:en).find_by_key %w{ foo.zero foo.one foo.other } - assert !I18n::Backend::ActiveRecord::Translation.locale(:en).find_by_key("foo") - end -end if defined?(ActiveRecord) diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/active_record_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/active_record_test.rb deleted file mode 100644 index 32f9a1a..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/active_record_test.rb +++ /dev/null @@ -1,52 +0,0 @@ -# encoding: utf-8 -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -setup_active_record - - -class I18nBackendActiveRecordTest < Test::Unit::TestCase - def setup - I18n.backend = I18n::Backend::ActiveRecord.new - store_translations(:en, :foo => { :bar => 'bar', :baz => 'baz' }) - end - - def teardown - I18n::Backend::ActiveRecord::Translation.destroy_all - super - end - - def test_store_translations_does_not_allow_ambigous_keys_1 - I18n::Backend::ActiveRecord::Translation.delete_all - I18n.backend.store_translations(:en, :foo => 'foo') - I18n.backend.store_translations(:en, :foo => { :bar => 'bar' }) - I18n.backend.store_translations(:en, :foo => { :baz => 'baz' }) - - translations = I18n::Backend::ActiveRecord::Translation.locale(:en).lookup('foo', '.').all - assert_equal %w(bar baz), translations.map(&:value) - - assert_equal({ :bar => 'bar', :baz => 'baz' }, I18n.t(:foo)) - end - - def test_store_translations_does_not_allow_ambigous_keys_2 - I18n::Backend::ActiveRecord::Translation.delete_all - I18n.backend.store_translations(:en, :foo => { :bar => 'bar' }) - I18n.backend.store_translations(:en, :foo => { :baz => 'baz' }) - I18n.backend.store_translations(:en, :foo => 'foo') - - translations = I18n::Backend::ActiveRecord::Translation.locale(:en).lookup('foo', '.').all - assert_equal %w(foo), translations.map(&:value) - - assert_equal 'foo', I18n.t(:foo) - end - - with_mocha do - def test_missing_translations_table_does_not_cause_available_locales_to_error - I18n::Backend::ActiveRecord::Translation.expects(:available_locales).raises(::ActiveRecord::StatementInvalid) - assert_equal [], I18n.backend.available_locales - end - end - - def test_expand_keys - assert_equal %w(foo foo.bar foo.bar.baz), I18n.backend.send(:expand_keys, :'foo.bar.baz') - end -end if defined?(ActiveRecord) \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/cache_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/cache_test.rb deleted file mode 100644 index d11beef..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/cache_test.rb +++ /dev/null @@ -1,72 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') -require 'i18n/backend/cache' - -begin - require 'active_support' -rescue LoadError - $stderr.puts "Skipping cache tests using ActiveSupport" -else - -class I18nBackendCacheTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::Cache - end - - def setup - I18n.backend = Backend.new - super - I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) - end - - def teardown - I18n.cache_store = nil - end - - def test_uses_cache - assert I18n.cache_store.is_a?(ActiveSupport::Cache::MemoryStore) - end - - with_mocha do - define_method "test translate hits the backend and caches the response" do - I18n.backend.expects(:lookup).returns('Foo') - assert_equal 'Foo', I18n.t(:foo) - - I18n.backend.expects(:lookup).never - assert_equal 'Foo', I18n.t(:foo) - - I18n.backend.expects(:lookup).returns('Bar') - assert_equal 'Bar', I18n.t(:bar) - end - - define_method "test still raises MissingTranslationData but also caches it" do - I18n.backend.expects(:lookup).returns(nil) - assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) } - - I18n.backend.expects(:lookup).never - assert_raises(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) } - end - end - - define_method "test uses 'i18n' as a cache key namespace by default" do - assert_equal 0, I18n.backend.send(:cache_key, :foo).index('i18n') - end - - define_method "test adds a custom cache key namespace" do - with_cache_namespace('bar') do - assert_equal 0, I18n.backend.send(:cache_key, :foo).index('i18n-bar') - end - end - - protected - - def with_cache_namespace(namespace) - I18n.cache_namespace = namespace - yield - I18n.cache_namespace = nil - end -end - -end # AS cache check diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/cascade_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/cascade_test.rb deleted file mode 100644 index 5a29cee..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/cascade_test.rb +++ /dev/null @@ -1,66 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nBackendCascadeTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::Cascade - end - - def setup - I18n.backend = Backend.new - store_translations(:en, - :foo => 'foo', - :bar => { :baz => 'baz' } - ) - end - - define_method "test: still returns an existing translation as usual" do - assert_equal 'foo', I18n.t(:foo) - assert_equal 'baz', I18n.t(:'bar.baz') - end - - define_method "test: falls back by cutting keys off the end of the scope" do - assert_equal 'foo', I18n.t(:'does_not_exist.foo') - assert_equal 'foo', I18n.t(:'does_not_exist.does_not_exist.foo') - - assert_equal 'baz', I18n.t(:'bar.does_not_exist.baz') - assert_equal 'baz', I18n.t(:'bar.does_not_exist.does_not_exist.baz') - end - - define_method "test: raises I18n::MissingTranslationData exception when no translation was found" do - assert_raises(I18n::MissingTranslationData) { I18n.t(:'foo.does_not_exist', :raise => true) } - assert_raises(I18n::MissingTranslationData) { I18n.t(:'bar.baz.does_not_exist', :raise => true) } - assert_raises(I18n::MissingTranslationData) { I18n.t(:'does_not_exist.bar.baz', :raise => true) } - end - - define_method "test: cascades before evaluating the default" do - assert_equal 'foo', I18n.t(:foo, :scope => :does_not_exist, :default => 'default') - end - - define_method "test: let's us assemble required fallbacks for ActiveRecord validation messages" do - store_translations(:en, - :errors => { - :reply => { - :title => { - :blank => 'blank on reply title' - }, - :taken => 'taken on reply' - }, - :topic => { - :title => { - :format => 'format on topic title' - }, - :length => 'length on topic' - }, - :odd => 'odd on errors' - } - ) - assert_equal 'blank on reply title', I18n.t(:'errors.reply.title.blank', :default => :'errors.topic.title.blank') - assert_equal 'taken on reply', I18n.t(:'errors.reply.title.taken', :default => :'errors.topic.title.taken') - assert_equal 'format on topic title', I18n.t(:'errors.reply.title.format', :default => :'errors.topic.title.format') - assert_equal 'length on topic', I18n.t(:'errors.reply.title.length', :default => :'errors.topic.title.length') - assert_equal 'odd on errors', I18n.t(:'errors.reply.title.odd', :default => :'errors.topic.title.odd') - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/chain_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/chain_test.rb deleted file mode 100644 index ab8e077..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/chain_test.rb +++ /dev/null @@ -1,64 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') -require 'i18n/backend/chain' - -class I18nBackendChainTest < Test::Unit::TestCase - def setup - @first = backend(:en => { - :foo => 'Foo', :formats => { :short => 'short' }, :plural_1 => { :one => '{{count}}' } - }) - @second = backend(:en => { - :bar => 'Bar', :formats => { :long => 'long' }, :plural_2 => { :one => 'one' } - }) - @chain = I18n.backend = I18n::Backend::Chain.new(@first, @second) - end - - define_method "test: looks up translations from the first chained backend" do - assert_equal 'Foo', @first.send(:translations)[:en][:foo] - assert_equal 'Foo', I18n.t(:foo) - end - - define_method "test: looks up translations from the second chained backend" do - assert_equal 'Bar', @second.send(:translations)[:en][:bar] - assert_equal 'Bar', I18n.t(:bar) - end - - define_method "test: defaults only apply to lookups on the last backend in the chain" do - assert_equal 'Foo', I18n.t(:foo, :default => 'Bah') - assert_equal 'Bar', I18n.t(:bar, :default => 'Bah') - assert_equal 'Bah', I18n.t(:bah, :default => 'Bah') # default kicks in only here - end - - define_method "test: default" do - assert_equal 'Fuh', I18n.t(:default => 'Fuh') - assert_equal 'Zero', I18n.t(:default => { :zero => 'Zero' }, :count => 0) - assert_equal({ :zero => 'Zero' }, I18n.t(:default => { :zero => 'Zero' })) - assert_equal 'Foo', I18n.t(:default => :foo) - end - - define_method "test: namespace lookup collects results from all backends" do - assert_equal({ :short => 'short', :long => 'long' }, I18n.t(:formats)) - end - - define_method "test: namespace lookup with only the first backend returning a result" do - assert_equal({ :one => '{{count}}' }, I18n.t(:plural_1)) - end - - define_method "test: pluralization still works" do - assert_equal '1', I18n.t(:plural_1, :count => 1) - assert_equal 'one', I18n.t(:plural_2, :count => 1) - end - - define_method "test: bulk lookup collects results from all backends" do - assert_equal ['Foo', 'Bar'], I18n.t([:foo, :bar]) - end - - protected - - def backend(translations) - backend = I18n::Backend::Simple.new - translations.each { |locale, translations| backend.store_translations(locale, translations) } - backend - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/fallbacks_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/fallbacks_test.rb deleted file mode 100644 index 3df6b06..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/fallbacks_test.rb +++ /dev/null @@ -1,57 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nBackendFallbacksTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::Fallbacks - end - - def setup - I18n.backend = Backend.new - store_translations(:en, :foo => 'Foo in :en', :bar => 'Bar in :en', :buz => 'Buz in :en') - store_translations(:de, :bar => 'Bar in :de', :baz => 'Baz in :de') - store_translations(:'de-DE', :baz => 'Baz in :de-DE') - end - - define_method "test: still returns an existing translation as usual" do - assert_equal 'Foo in :en', I18n.t(:foo, :locale => :en) - assert_equal 'Bar in :de', I18n.t(:bar, :locale => :de) - assert_equal 'Baz in :de-DE', I18n.t(:baz, :locale => :'de-DE') - end - - define_method "test: returns the :en translation for a missing :de translation" do - assert_equal 'Foo in :en', I18n.t(:foo, :locale => :de) - end - - define_method "test: returns the :de translation for a missing :'de-DE' translation" do - assert_equal 'Bar in :de', I18n.t(:bar, :locale => :'de-DE') - end - - define_method "test: returns the :en translation for translation missing in both :de and :'de-De'" do - assert_equal 'Buz in :en', I18n.t(:buz, :locale => :'de-DE') - end - - define_method "test: raises I18n::MissingTranslationData exception when no translation was found" do - assert_raises(I18n::MissingTranslationData) { I18n.t(:faa, :locale => :en, :raise => true) } - assert_raises(I18n::MissingTranslationData) { I18n.t(:faa, :locale => :de, :raise => true) } - end -end - -class I18nBackendFallbacksWithChainTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::Fallbacks - end - - def setup - backend = Backend.new - backend.store_translations(:de, :foo => 'FOO') - I18n.backend = I18n::Backend::Chain.new(I18n::Backend::Simple.new, backend) - end - - define_method "test: falls back from de-DE to de when there is no translation for de-DE available" do - assert_equal 'FOO', I18n.t(:foo, :locale => :'de-DE') - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/fast_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/fast_test.rb deleted file mode 100644 index 5768857..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/fast_test.rb +++ /dev/null @@ -1,50 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') -require File.expand_path(File.dirname(__FILE__) + '/simple_test') - -class I18nBackendFastTest < I18nBackendSimpleTest - class FastBackend - include I18n::Backend::Base - include I18n::Backend::Fast - end - - def setup - super - I18n.backend = FastBackend.new - end -end - -class I18nBackendFastSpecificTest < Test::Unit::TestCase - class FastBackend - include I18n::Backend::Base - include I18n::Backend::Fast - end - - def setup - @backend = FastBackend.new - end - - def assert_flattens(expected, nested) - assert_equal expected, @backend.send(:flatten_hash, nested) - end - - def test_hash_flattening_works - assert_flattens( - {:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}, :"b.f" => {:x=>"x"}, :"b.c"=>"c", :"b.f.x"=>"x", :"b.d"=>"d"}, - {:a=>'a', :b=>{:c=>'c', :d=>'d', :f=>{:x=>'x'}}} - ) - assert_flattens({:a=>{:b =>['a', 'b']}, :"a.b"=>['a', 'b']}, {:a=>{:b =>['a', 'b']}}) - end - - def test_pluralization_logic_and_lookup_works - counts_hash = {:zero => 'zero', :one => 'one', :other => 'other'} - @backend.store_translations :en, {:a => counts_hash} - assert_equal 'one', @backend.translate(:en, :a, :count => 1) - end - - def test_translation_subtree_retrieval - @backend.store_translations :en, :a => {:foo => 'bar'} - assert_equal({:foo => 'bar'}, @backend.translate(:en, :a)) - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/helpers_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/helpers_test.rb deleted file mode 100644 index e8d427f..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/helpers_test.rb +++ /dev/null @@ -1,26 +0,0 @@ -# encoding: utf-8 -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nBackendHelpersTest < Test::Unit::TestCase - def setup - @backend = I18n::Backend::Simple.new - end - - def test_wind_keys - hash = { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"} - expected = { :"a.b.c" => "d", :"a.b.e" => "f", :"a.g" => "h", :"i" => "j" } - assert_equal expected, @backend.wind_keys(hash) - end - - def test_unwind_keys - hash = { "a.b.c" => "d", :"a.b.e" => "f", :"a.g" => "h", "i" => "j" } - expected = { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"} - assert_equal expected, @backend.unwind_keys(hash) - end - - def test_deep_symbolize_keys - result = @backend.deep_symbolize_keys('foo' => { 'bar' => { 'baz' => 'bar' } }) - expected = {:foo => {:bar => {:baz => 'bar'}}} - assert_equal expected, result - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/interpolation_compiler_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/interpolation_compiler_test.rb deleted file mode 100644 index 38d272e..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/interpolation_compiler_test.rb +++ /dev/null @@ -1,107 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class InterpolationCompilerTest < Test::Unit::TestCase - Compiler = I18n::Backend::InterpolationCompiler::Compiler - - def compile_and_interpolate(str, values = {}) - Compiler.compile_if_an_interpolation(str).i18n_interpolate(values) - end - - def assert_escapes_interpolation_key(expected, malicious_str) - assert_equal(expected, Compiler.send(:escape_key_sym, malicious_str)) - end - - def test_escape_key_properly_escapes - assert_escapes_interpolation_key ':"\""', '"' - assert_escapes_interpolation_key ':"\\\\"', '\\' - assert_escapes_interpolation_key ':"\\\\\""', '\\"' - assert_escapes_interpolation_key ':"\#{}"', '#{}' - assert_escapes_interpolation_key ':"\\\\\#{}"', '\#{}' - end - - def assert_escapes_plain_string(expected, plain_str) - assert_equal expected, Compiler.send(:escape_plain_str, plain_str) - end - - def test_escape_plain_string_properly_escapes - assert_escapes_plain_string '\\"', '"' - assert_escapes_plain_string '\'', '\'' - assert_escapes_plain_string '\\#', '#' - assert_escapes_plain_string '\\#{}', '#{}' - assert_escapes_plain_string '\\\\\\"','\\"' - end - - def test_non_interpolated_strings_or_arrays_dont_get_compiled - ['abc', '\\{a}}', '{a}}', []].each do |obj| - Compiler.compile_if_an_interpolation(obj) - assert_equal false, obj.respond_to?(:i18n_interpolate) - end - end - - def test_interpolated_string_gets_compiled - assert_equal '-A-', compile_and_interpolate('-{{a}}-', :a => 'A') - end - - def assert_handles_key(str, key) - assert_equal 'A', compile_and_interpolate(str, key => 'A') - end - - def test_compiles_fancy_keys - assert_handles_key('{{\}}', :'\\' ) - assert_handles_key('{{#}}', :'#' ) - assert_handles_key('{{#{}}', :'#{' ) - assert_handles_key('{{#$SAFE}}', :'#$SAFE') - assert_handles_key('{{\000}}', :'\000' ) - assert_handles_key('{{\'}}', :'\'' ) - assert_handles_key('{{\'\'}}', :'\'\'' ) - assert_handles_key('{{a.b}}', :'a.b' ) - assert_handles_key('{{ }}', :' ' ) - assert_handles_key('{{:}}', :':' ) - assert_handles_key("{{:''}}", :":''" ) - assert_handles_key('{{:"}}', :':"' ) - end - - def test_str_containing_only_escaped_interpolation_is_handled_correctly - assert_equal 'abc {{x}}', compile_and_interpolate('abc \\{{x}}') - end - - def test_handles_weired_strings - assert_equal '#{} a', compile_and_interpolate('#{} {{a}}', :a => 'a') - assert_equal '"#{abc}"', compile_and_interpolate('"#{ab{{a}}c}"', :a => '' ) - assert_equal 'a}', compile_and_interpolate('{{{a}}}', :'{a' => 'a') - assert_equal '"', compile_and_interpolate('"{{a}}', :a => '' ) - assert_equal 'a{{a}}', compile_and_interpolate('{{a}}\\{{a}}', :a => 'a') - assert_equal '\\{{a}}', compile_and_interpolate('\\\\{{a}}') - assert_equal '\";eval("a")', compile_and_interpolate('\";eval("{{a}}")', :a => 'a') - assert_equal '\";eval("a")', compile_and_interpolate('\";eval("a"){{a}}',:a => '' ) - assert_equal "\na", compile_and_interpolate("\n{{a}}", :a => 'a') - end -end - -class I18nBackendInterpolationCompilerTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::InterpolationCompiler - end - - include Tests::Api::Interpolation - - def setup - I18n.backend = Backend.new - super - end - - # pre-compile default strings to make sure we are testing I18n::Backend::InterpolationCompiler - def interpolate(*args) - options = args.last.kind_of?(Hash) ? args.last : {} - if default_str = options[:default] - I18n::Backend::InterpolationCompiler::Compiler.compile_if_an_interpolation(default_str) - end - super - end - - # I kinda don't think this really is a correct behavior - undef :'test interpolation: given no values it does not alter the string' -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/metadata_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/metadata_test.rb deleted file mode 100644 index fd53969..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/metadata_test.rb +++ /dev/null @@ -1,67 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') -require 'i18n/backend/metadata' - -class I18nBackendMetadataTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::Metadata - end - - def setup - I18n.backend = Backend.new - store_translations(:en, :foo => 'Hi {{name}}') - end - - define_method "test: translation strings carry metadata" do - translation = I18n.t(:foo) - assert translation.respond_to?(:translation_metadata) - assert translation.translation_metadata.is_a?(Hash) - end - - define_method "test: translate preserves metadata stored on original Strings" do - store_metadata(:foo, :bar, 'bar') - assert_equal 'bar', I18n.t(:foo).translation_metadata[:bar] - end - - define_method "test: translate preserves metadata stored on original Strings (when interpolated)" do - store_metadata(:foo, :bar, 'bar') - assert_equal 'bar', I18n.t(:foo, :name => 'David').translation_metadata[:bar] - end - - define_method "test: translate adds the locale to metadata on Strings" do - assert_equal :en, I18n.t(:foo, :locale => :en).translation_metadata[:locale] - end - - define_method "test: translate adds the key to metadata on Strings" do - assert_equal :foo, I18n.t(:foo).translation_metadata[:key] - end - - define_method "test: translate adds the default to metadata on Strings" do - assert_equal 'bar', I18n.t(:foo, :default => 'bar', :name => '').translation_metadata[:default] - end - - define_method "test: translation adds the interpolation values to metadata on Strings" do - assert_equal({:name => 'David'}, I18n.t(:foo, :name => 'David').translation_metadata[:values]) - end - - define_method "test: interpolation adds the original string to metadata on Strings" do - assert_equal('Hi {{name}}', I18n.t(:foo, :name => 'David').translation_metadata[:original]) - end - - define_method "test: pluralizatoin adds the count to metadata on Strings" do - assert_equal(1, I18n.t(:missing, :count => 1, :default => { :one => 'foo' }).translation_metadata[:count]) - end - - protected - - def translations - I18n.backend.instance_variable_get(:@translations) - end - - def store_metadata(key, name, value) - translations[:en][key].translation_metadata[name] = value - end -end - diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/pluralization_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/pluralization_test.rb deleted file mode 100644 index f0bf4b7..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/pluralization_test.rb +++ /dev/null @@ -1,43 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') -require 'i18n/backend/pluralization' - -class I18nBackendPluralizationTest < Test::Unit::TestCase - class Backend - include I18n::Backend::Base - include I18n::Backend::Pluralization - end - - def setup - I18n.backend = Backend.new - @rule = lambda { |n| n == 1 ? :one : n == 0 || (2..10).include?(n % 100) ? :few : (11..19).include?(n % 100) ? :many : :other } - store_translations(:foo, :i18n => { :plural => { :rule => @rule } }) - @entry = { :zero => 'zero', :one => 'one', :few => 'few', :many => 'many', :other => 'other' } - end - - define_method "test: pluralization picks a pluralizer from :'i18n.pluralize'" do - assert_equal @rule, I18n.backend.send(:pluralizer, :foo) - end - - define_method "test: pluralization picks :one for 1" do - assert_equal 'one', I18n.t(:count => 1, :default => @entry, :locale => :foo) - end - - define_method "test: pluralization picks :few for 2" do - assert_equal 'few', I18n.t(:count => 2, :default => @entry, :locale => :foo) - end - - define_method "test: pluralization picks :many for 11" do - assert_equal 'many', I18n.t(:count => 11, :default => @entry, :locale => :foo) - end - - define_method "test: pluralization picks zero for 0 if the key is contained in the data" do - assert_equal 'zero', I18n.t(:count => 0, :default => @entry, :locale => :foo) - end - - define_method "test: pluralization picks few for 0 if the key is not contained in the data" do - @entry.delete(:zero) - assert_equal 'few', I18n.t(:count => 0, :default => @entry, :locale => :foo) - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/simple_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/simple_test.rb deleted file mode 100644 index c9a0324..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/backend/simple_test.rb +++ /dev/null @@ -1,77 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -class I18nBackendSimpleTest < Test::Unit::TestCase - def setup - I18n.backend = I18n::Backend::Simple.new - I18n.load_path = [locales_dir + '/en.yml'] - end - - # useful because this way we can use the backend with no key for interpolation/pluralization - define_method "test simple backend lookup: given nil as a key it returns nil" do - assert_nil I18n.backend.send(:lookup, :en, nil) - end - - # loading translations - - define_method "test simple load_translations: given an unknown file type it raises I18n::UnknownFileType" do - assert_raises(I18n::UnknownFileType) { I18n.backend.load_translations("#{locales_dir}/en.xml") } - end - - define_method "test simple load_translations: given a Ruby file name it does not raise anything" do - assert_nothing_raised { I18n.backend.load_translations("#{locales_dir}/en.rb") } - end - - define_method "test simple load_rb: loads data from a Ruby file" do - data = I18n.backend.send(:load_rb, "#{locales_dir}/en.rb") - assert_equal({ :en => { :fuh => { :bah => 'bas' } } }, data) - end - - define_method "test simple load_yml: loads data from a YAML file" do - data = I18n.backend.send(:load_yml, "#{locales_dir}/en.yml") - assert_equal({ 'en' => { 'foo' => { 'bar' => 'baz' } } }, data) - end - - define_method "test simple load_translations: loads data from known file formats" do - I18n.backend = I18n::Backend::Simple.new - I18n.backend.load_translations("#{locales_dir}/en.rb", "#{locales_dir}/en.yml") - expected = { :en => { :fuh => { :bah => "bas" }, :foo => { :bar => "baz" } } } - assert_equal expected, translations - end - - # storing translations - - define_method "test simple store_translations: stores translations, ... no, really :-)" do - I18n.backend.store_translations :'en', :foo => 'bar' - assert_equal Hash[:'en', {:foo => 'bar'}], translations - end - - define_method "test simple store_translations: deep_merges with existing translations" do - I18n.backend.store_translations :'en', :foo => {:bar => 'bar'} - I18n.backend.store_translations :'en', :foo => {:baz => 'baz'} - assert_equal Hash[:'en', {:foo => {:bar => 'bar', :baz => 'baz'}}], translations - end - - define_method "test simple store_translations: converts the given locale to a Symbol" do - I18n.backend.store_translations 'en', :foo => 'bar' - assert_equal Hash[:'en', {:foo => 'bar'}], translations - end - - define_method "test simple store_translations: converts keys to Symbols" do - I18n.backend.store_translations 'en', 'foo' => {'bar' => 'bar', 'baz' => 'baz'} - assert_equal Hash[:'en', {:foo => {:bar => 'bar', :baz => 'baz'}}], translations - end - - # reloading translations - - define_method "test simple reload_translations: unloads translations" do - I18n.backend.reload! - assert_nil translations - end - - define_method "test simple reload_translations: uninitializes the backend" do - I18n.backend.reload! - assert_equal I18n.backend.initialized?, false - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/core_ext/string/interpolate_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/core_ext/string/interpolate_test.rb deleted file mode 100644 index 0906307..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/core_ext/string/interpolate_test.rb +++ /dev/null @@ -1,94 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../../test_helper') - -# thanks to Masao's String extensions these should work the same in -# Ruby 1.8 (patched) and Ruby 1.9 (native) -# some tests taken from Masao's tests -# http://github.com/mutoh/gettext/blob/edbbe1fa8238fa12c7f26f2418403015f0270e47/test/test_string.rb - -class I18nCoreExtStringInterpolationTest < Test::Unit::TestCase - define_method "test: String interpolates a single argument" do - assert_equal "Masao", "%s" % "Masao" - end - - define_method "test: String interpolates an array argument" do - assert_equal "1 message", "%d %s" % [1, 'message'] - end - - define_method "test: String interpolates a hash argument w/ named placeholders" do - assert_equal "Masao Mutoh", "%{first} %{last}" % { :first => 'Masao', :last => 'Mutoh' } - end - - define_method "test: String interpolates a hash argument w/ named placeholders (reverse order)" do - assert_equal "Mutoh, Masao", "%{last}, %{first}" % { :first => 'Masao', :last => 'Mutoh' } - end - - define_method "test: String interpolates named placeholders with sprintf syntax" do - assert_equal "10, 43.4", "%d, %.1f" % {:integer => 10, :float => 43.4} - end - - define_method "test: String interpolates named placeholders with sprintf syntax, does not recurse" do - assert_equal "%s", "%{msg}" % { :msg => '%s', :not_translated => 'should not happen' } - end - - define_method "test: String interpolation does not replace anything when no placeholders are given" do - assert_equal("aaa", "aaa" % {:num => 1}) - assert_equal("bbb", "bbb" % [1]) - end - - define_method "test: String interpolation sprintf behaviour equals Ruby 1.9 behaviour" do - assert_equal("1", "%d" % {:num => 1}) - assert_equal("0b1", "%#b" % {:num => 1}) - assert_equal("foo", "%s" % {:msg => "foo"}) - assert_equal("1.000000", "%f" % {:num => 1.0}) - assert_equal(" 1", "%3.0f" % {:num => 1.0}) - assert_equal("100.00", "%2.2f" % {:num => 100.0}) - assert_equal("0x64", "%#x" % {:num => 100.0}) - assert_raise(ArgumentError) { "%,d" % {:num => 100} } - assert_raise(ArgumentError) { "%/d" % {:num => 100} } - end - - define_method "test: String interpolation old-style sprintf still works" do - assert_equal("foo 1.000000", "%s %f" % ["foo", 1.0]) - end - - define_method "test: String interpolation raises an ArgumentError when the string has extra placeholders (Array)" do - assert_raises(ArgumentError) do # Ruby 1.9 msg: "too few arguments" - "%s %s" % %w(Masao) - end - end - - define_method "test: String interpolation raises a KeyError when the string has extra placeholders (Hash)" do - assert_raises(KeyError) do # Ruby 1.9 msg: "key not found" - "%{first} %{last}" % { :first => 'Masao' } - end - end - - define_method "test: String interpolation does not raise when passed extra values (Array)" do - assert_nothing_raised do - assert_equal "Masao", "%s" % %w(Masao Mutoh) - end - end - - define_method "test: String interpolation does not raise when passed extra values (Hash)" do - assert_nothing_raised do - assert_equal "Masao Mutoh", "%{first} %{last}" % { :first => 'Masao', :last => 'Mutoh', :salutation => 'Mr.' } - end - end - - define_method "test: % acts as escape character in String interpolation" do - assert_equal "%{first}", "%%{first}" % { :first => 'Masao' } - assert_equal("% 1", "%% %d" % {:num => 1.0}) - assert_equal("%{num} %d", "%%{num} %%d" % {:num => 1}) - end - - def test_sprintf_mix_unformatted_and_formatted_named_placeholders - assert_equal("foo 1.000000", "%{name} %f" % {:name => "foo", :num => 1.0}) - end - - def test_string_interpolation_raises_an_argument_error_when_mixing_named_and_unnamed_placeholders - assert_raises(ArgumentError) { "%{name} %f" % [1.0] } - assert_raises(ArgumentError) { "%{name} %f" % [1.0, 2.0] } - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/gettext/api_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/gettext/api_test.rb deleted file mode 100644 index b9c724e..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/gettext/api_test.rb +++ /dev/null @@ -1,201 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') -require 'i18n/backend/gettext' -require 'i18n/helpers/gettext' - -include I18n::Helpers::Gettext - -class I18nGettextApiTest < Test::Unit::TestCase - def setup - I18n.locale = :en - I18n.backend.store_translations :de, { - 'Hi Gettext!' => 'Hallo Gettext!', - 'Sentence 1. Sentence 2.' => 'Satz 1. Satz 2.', - "An apple" => { :one => 'Ein Apfel', :other => '{{count}} Äpfel' }, - :special => { "A special apple" => { :one => 'Ein spezieller Apfel', :other => '{{count}} spezielle Äpfel' } }, - :foo => { :bar => 'bar-de' } - } - end - - # gettext - def test_gettext_uses_msg_as_default - assert_equal 'Hi Gettext!', _('Hi Gettext!') - end - - def test_gettext_uses_msg_as_key - I18n.locale = :de - assert_equal 'Hallo Gettext!', gettext('Hi Gettext!') - assert_equal 'Hallo Gettext!', _('Hi Gettext!') - end - - def test_gettext_uses_msg_containing_dots_as_default - assert_equal 'Sentence 1. Sentence 2.', gettext('Sentence 1. Sentence 2.') - assert_equal 'Sentence 1. Sentence 2.', _('Sentence 1. Sentence 2.') - end - - def test_gettext_uses_msg_containing_dots_as_key - I18n.locale = :de - assert_equal 'Satz 1. Satz 2.', gettext('Sentence 1. Sentence 2.') - assert_equal 'Satz 1. Satz 2.', _('Sentence 1. Sentence 2.') - end - - # sgettext - def test_sgettext_defaults_to_the_last_token_of_a_scoped_msgid - assert_equal 'bar', sgettext('foo|bar') - assert_equal 'bar', s_('foo|bar') - end - - def test_sgettext_looks_up_a_scoped_translation - I18n.locale = :de - assert_equal 'bar-de', sgettext('foo|bar') - assert_equal 'bar-de', s_('foo|bar') - end - - # pgettext - def test_pgettext_defaults_to_msgid - assert_equal 'bar', pgettext('foo', 'bar') - assert_equal 'bar', p_('foo', 'bar') - end - - def test_pgettext_looks_up_a_scoped_translation - I18n.locale = :de - assert_equal 'bar-de', pgettext('foo', 'bar') - assert_equal 'bar-de', p_('foo', 'bar') - end - - # ngettext - def test_ngettext_looks_up_msg_id_as_default_singular - assert_equal 'An apple', ngettext('An apple', '{{count}} apples', 1) - assert_equal 'An apple', n_('An apple', '{{count}} apples', 1) - end - - def test_ngettext_looks_up_msg_id_plural_as_default_plural - assert_equal '2 apples', ngettext('An apple', '{{count}} apples', 2) - assert_equal '2 apples', n_('An apple', '{{count}} apples', 2) - end - - def test_ngettext_looks_up_a_singular - I18n.locale = :de - assert_equal 'Ein Apfel', ngettext('An apple', '{{count}} apples', 1) - assert_equal 'Ein Apfel', n_('An apple', '{{count}} apples', 1) - end - - def test_ngettext_looks_up_a_plural - I18n.locale = :de - assert_equal '2 Äpfel', ngettext('An apple', '{{count}} apples', 2) - assert_equal '2 Äpfel', n_('An apple', '{{count}} apples', 2) - end - - def test_ngettext_looks_up_msg_id_as_default_singular_with_alternative_syntax - assert_equal 'An apple', ngettext(['An apple', '{{count}} apples'], 1) - assert_equal 'An apple', n_(['An apple', '{{count}} apples'], 1) - end - - def test_ngettext_looks_up_msg_id_plural_as_default_plural_with_alternative_syntax - assert_equal '2 apples', ngettext(['An apple', '{{count}} apples'], 2) - assert_equal '2 apples', n_(['An apple', '{{count}} apples'], 2) - end - - def test_ngettext_looks_up_a_singular_with_alternative_syntax - I18n.locale = :de - assert_equal 'Ein Apfel', ngettext(['An apple', '{{count}} apples'], 1) - assert_equal 'Ein Apfel', n_(['An apple', '{{count}} apples'], 1) - end - - def test_ngettext_looks_up_a_plural_with_alternative_syntax - I18n.locale = :de - assert_equal '2 Äpfel', ngettext(['An apple', '{{count}} apples'], 2) - assert_equal '2 Äpfel', n_(['An apple', '{{count}} apples'], 2) - end - - # nsgettext - def test_nsgettext_looks_up_msg_id_as_default_singular - assert_equal 'A special apple', nsgettext('special|A special apple', '{{count}} special apples', 1) - assert_equal 'A special apple', ns_('special|A special apple', '{{count}} special apples', 1) - end - - def test_nsgettext_looks_up_msg_id_plural_as_default_plural - assert_equal '2 special apples', nsgettext('special|A special apple', '{{count}} special apples', 2) - assert_equal '2 special apples', ns_('special|A special apple', '{{count}} special apples', 2) - end - - def test_nsgettext_looks_up_a_singular - I18n.locale = :de - assert_equal 'Ein spezieller Apfel', nsgettext('special|A special apple', '{{count}} special apples', 1) - assert_equal 'Ein spezieller Apfel', ns_('special|A special apple', '{{count}} special apples', 1) - end - - def test_nsgettext_looks_up_a_plural - I18n.locale = :de - assert_equal '2 spezielle Äpfel', nsgettext('special|A special apple', '{{count}} special apples', 2) - assert_equal '2 spezielle Äpfel', ns_('special|A special apple', '{{count}} special apples', 2) - end - - def test_nsgettext_looks_up_msg_id_as_default_singular_with_alternative_syntax - assert_equal 'A special apple', nsgettext(['special|A special apple', '{{count}} special apples'], 1) - assert_equal 'A special apple', ns_(['special|A special apple', '{{count}} special apples'], 1) - end - - def test_nsgettext_looks_up_msg_id_plural_as_default_plural_with_alternative_syntax - assert_equal '2 special apples', nsgettext(['special|A special apple', '{{count}} special apples'], 2) - assert_equal '2 special apples', ns_(['special|A special apple', '{{count}} special apples'], 2) - end - - def test_nsgettext_looks_up_a_singular_with_alternative_syntax - I18n.locale = :de - assert_equal 'Ein spezieller Apfel', nsgettext(['special|A special apple', '{{count}} special apples'], 1) - assert_equal 'Ein spezieller Apfel', ns_(['special|A special apple', '{{count}} special apples'], 1) - end - - def test_nsgettext_looks_up_a_plural_with_alternative_syntax - I18n.locale = :de - assert_equal '2 spezielle Äpfel', nsgettext(['special|A special apple', '{{count}} special apples'], 2) - assert_equal '2 spezielle Äpfel', ns_(['special|A special apple', '{{count}} special apples'], 2) - end - - # npgettext - def test_npgettext_looks_up_msg_id_as_default_singular - assert_equal 'A special apple', npgettext('special', 'A special apple', '{{count}} special apples', 1) - assert_equal 'A special apple', np_('special', 'A special apple', '{{count}} special apples', 1) - end - - def test_npgettext_looks_up_msg_id_plural_as_default_plural - assert_equal '2 special apples', npgettext('special', 'A special apple', '{{count}} special apples', 2) - assert_equal '2 special apples', np_('special', 'A special apple', '{{count}} special apples', 2) - end - - def test_npgettext_looks_up_a_singular - I18n.locale = :de - assert_equal 'Ein spezieller Apfel', npgettext('special', 'A special apple', '{{count}} special apples', 1) - assert_equal 'Ein spezieller Apfel', np_('special', 'A special apple', '{{count}} special apples', 1) - end - - def test_npgettext_looks_up_a_plural - I18n.locale = :de - assert_equal '2 spezielle Äpfel', npgettext('special', 'A special apple', '{{count}} special apples', 2) - assert_equal '2 spezielle Äpfel', np_('special', 'A special apple', '{{count}} special apples', 2) - end - - def test_npgettext_looks_up_msg_id_as_default_singular_with_alternative_syntax - assert_equal 'A special apple', npgettext('special', ['A special apple', '{{count}} special apples'], 1) - assert_equal 'A special apple', np_('special', ['A special apple', '{{count}} special apples'], 1) - end - - def test_npgettext_looks_up_msg_id_plural_as_default_plural_with_alternative_syntax - assert_equal '2 special apples', npgettext('special', ['A special apple', '{{count}} special apples'], 2) - assert_equal '2 special apples', np_('special', ['A special apple', '{{count}} special apples'], 2) - end - - def test_npgettext_looks_up_a_singular_with_alternative_syntax - I18n.locale = :de - assert_equal 'Ein spezieller Apfel', npgettext('special', ['A special apple', '{{count}} special apples'], 1) - assert_equal 'Ein spezieller Apfel', np_('special', ['A special apple', '{{count}} special apples'], 1) - end - - def test_npgettext_looks_up_a_plural_with_alternative_syntax - I18n.locale = :de - assert_equal '2 spezielle Äpfel', npgettext('special', ['A special apple', '{{count}} special apples'], 2) - assert_equal '2 spezielle Äpfel', np_('special', ['A special apple', '{{count}} special apples'], 2) - end -end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/gettext/backend_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/gettext/backend_test.rb deleted file mode 100644 index cf61b4e..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/gettext/backend_test.rb +++ /dev/null @@ -1,91 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') -require 'i18n/backend/gettext' -require 'i18n/helpers/gettext' - -class I18nGettextBackendTest < Test::Unit::TestCase - include I18n::Helpers::Gettext - - class Backend - include I18n::Backend::Base - include I18n::Backend::Gettext - end - - def setup - I18n.backend = Backend.new - I18n.locale = :en - I18n.load_path = ["#{locales_dir}/de.po"] - end - - def teardown - I18n.load_path = nil - I18n.backend = nil - end - - def test_backend_loads_po_file - I18n.backend.send(:init_translations) - assert I18n.backend.send(:translations)[:de][:"Axis"] - end - - def test_looks_up_a_translation - I18n.locale = :de - assert_equal 'Auto', gettext('car') - end - - def test_uses_default_translation - assert_equal 'car', gettext('car') - end - - def test_looks_up_a_namespaced_translation - I18n.locale = :de - assert_equal 'Räderzahl', sgettext('Car|Wheels count') - assert_equal 'Räderzahl', pgettext('Car', 'Wheels count') - end - - def test_uses_namespaced_default_translation - assert_equal 'Wheels count', sgettext('Car|Wheels count') - assert_equal 'Wheels count', pgettext('Car', 'Wheels count') - end - - def test_pluralizes_entry - I18n.locale = :de - assert_equal 'Achse', ngettext('Axis', 'Axis', 1) - assert_equal 'Achsen', ngettext('Axis', 'Axis', 2) - end - - def test_pluralizes_default_entry - assert_equal 'Axis', ngettext('Axis', 'Axis', 1) - assert_equal 'Axis', ngettext('Axis', 'Axis', 2) - end - - def test_pluralizes_namespaced_entry - I18n.locale = :de - assert_equal 'Rad', nsgettext('Car|wheel', 'wheels', 1) - assert_equal 'Räder', nsgettext('Car|wheel', 'wheels', 2) - assert_equal 'Rad', npgettext('Car', 'wheel', 'wheels', 1) - assert_equal 'Räder', npgettext('Car', 'wheel', 'wheels', 2) - end - - def test_pluralizes_namespaced_default_entry - assert_equal 'wheel', nsgettext('Car|wheel', 'wheels', 1) - assert_equal 'wheels', nsgettext('Car|wheel', 'wheels', 2) - assert_equal 'wheel', npgettext('Car', 'wheel', 'wheels', 1) - assert_equal 'wheels', npgettext('Car', 'wheel', 'wheels', 2) - end - - def test_pluralizes_namespaced_entry_with_alternative_syntax - I18n.locale = :de - assert_equal 'Rad', nsgettext(['Car|wheel', 'wheels'], 1) - assert_equal 'Räder', nsgettext(['Car|wheel', 'wheels'], 2) - assert_equal 'Rad', npgettext('Car', ['wheel', 'wheels'], 1) - assert_equal 'Räder', npgettext('Car', ['wheel', 'wheels'], 2) - end - - def test_ngettextpluralizes_entry_with_dots - I18n.locale = :de - assert_equal 'Auf 1 Achse.', n_("On %{count} wheel.", "On %{count} wheels.", 1) - assert_equal 'Auf 2 Achsen.', n_("On %{count} wheel.", "On %{count} wheels.", 2) - end - -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_exceptions_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_exceptions_test.rb deleted file mode 100644 index e492520..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_exceptions_test.rb +++ /dev/null @@ -1,97 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../test_helper') - -class I18nExceptionsTest < Test::Unit::TestCase - def test_invalid_locale_stores_locale - force_invalid_locale - rescue I18n::ArgumentError => e - assert_nil e.locale - end - - def test_invalid_locale_message - force_invalid_locale - rescue I18n::ArgumentError => e - assert_equal 'nil is not a valid locale', e.message - end - - def test_missing_translation_data_stores_locale_key_and_options - force_missing_translation_data - rescue I18n::ArgumentError => e - options = {:scope => :bar} - assert_equal 'de', e.locale - assert_equal :foo, e.key - assert_equal options, e.options - end - - def test_missing_translation_data_message - force_missing_translation_data - rescue I18n::ArgumentError => e - assert_equal 'translation missing: de, bar, foo', e.message - end - - def test_invalid_pluralization_data_stores_entry_and_count - force_invalid_pluralization_data - rescue I18n::ArgumentError => e - assert_equal [:bar], e.entry - assert_equal 1, e.count - end - - def test_invalid_pluralization_data_message - force_invalid_pluralization_data - rescue I18n::ArgumentError => e - assert_equal 'translation data [:bar] can not be used with :count => 1', e.message - end - - def test_missing_interpolation_argument_stores_key_and_string - assert_raises(I18n::MissingInterpolationArgument) { force_missing_interpolation_argument } - force_missing_interpolation_argument - rescue I18n::ArgumentError => e - # assert_equal :bar, e.key - assert_equal "{{bar}}", e.string - end - - def test_missing_interpolation_argument_message - force_missing_interpolation_argument - rescue I18n::ArgumentError => e - assert_equal 'missing interpolation argument in "{{bar}}" ({:baz=>"baz"} given)', e.message - end - - def test_reserved_interpolation_key_stores_key_and_string - force_reserved_interpolation_key - rescue I18n::ArgumentError => e - assert_equal :scope, e.key - assert_equal "{{scope}}", e.string - end - - def test_reserved_interpolation_key_message - force_reserved_interpolation_key - rescue I18n::ArgumentError => e - assert_equal 'reserved key :scope used in "{{scope}}"', e.message - end - - private - def force_invalid_locale - I18n.backend.translate nil, :foo - end - - def force_missing_translation_data - I18n.backend.store_translations 'de', :bar => nil - I18n.backend.translate 'de', :foo, :scope => :bar - end - - def force_invalid_pluralization_data - I18n.backend.store_translations 'de', :foo => [:bar] - I18n.backend.translate 'de', :foo, :count => 1 - end - - def force_missing_interpolation_argument - I18n.backend.store_translations 'de', :foo => "{{bar}}" - I18n.backend.translate 'de', :foo, :baz => 'baz' - end - - def force_reserved_interpolation_key - I18n.backend.store_translations 'de', :foo => "{{scope}}" - I18n.backend.translate 'de', :foo, :baz => 'baz' - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_load_path_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_load_path_test.rb deleted file mode 100644 index 3b92528..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_load_path_test.rb +++ /dev/null @@ -1,23 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../test_helper') - -class I18nLoadPathTest < Test::Unit::TestCase - # include Tests::Backend::Simple::Setup::Base - - def setup - I18n.locale = :en - I18n.backend = I18n::Backend::Simple.new - store_translations(:en, :foo => {:bar => 'bar', :baz => 'baz'}) - end - - def test_nested_load_paths_do_not_break_locale_loading - I18n.load_path = [[locales_dir + '/en.yml']] - assert_equal "baz", I18n.t(:'foo.bar') - end - - def test_adding_arrays_of_filenames_to_load_path_do_not_break_locale_loading - I18n.load_path << Dir[locales_dir + '/*.{rb,yml}'] - assert_equal "baz", I18n.t(:'foo.bar') - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_test.rb deleted file mode 100644 index 2594837..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/i18n_test.rb +++ /dev/null @@ -1,172 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../test_helper') - -class I18nTest < Test::Unit::TestCase - def setup - I18n.backend.store_translations :'en', { - :currency => { - :format => { - :separator => '.', - :delimiter => ',', - } - } - } - end - - def test_uses_simple_backend_set_by_default - assert I18n.backend.is_a?(I18n::Backend::Simple) - end - - def test_can_set_backend - assert_nothing_raised { I18n.backend = self } - assert_equal self, I18n.backend - I18n.backend = I18n::Backend::Simple.new - end - - def test_uses_en_us_as_default_locale_by_default - assert_equal :en, I18n.default_locale - end - - def test_can_set_default_locale - assert_nothing_raised { I18n.default_locale = 'de' } - assert_equal :de, I18n.default_locale - I18n.default_locale = :en - end - - def test_uses_default_locale_as_locale_by_default - assert_equal I18n.default_locale, I18n.locale - end - - def test_can_set_locale_to_thread_current - assert_nothing_raised { I18n.locale = 'de' } - assert_equal :de, I18n.locale - assert_equal :de, Thread.current[:locale] - I18n.locale = :en - end - - def test_defaults_to_dot_as_separator - assert_equal '.', I18n.default_separator - end - - def test_can_set_default_separator - assert_nothing_raised { I18n.default_separator = "\001" } - I18n.default_separator = '.' # revert it - end - - def test_normalize_keys - assert_equal [:en, :foo, :bar], I18n.send(:normalize_translation_keys, :en, :bar, :foo) - assert_equal [:en, :foo, :bar, :baz, :buz], I18n.send(:normalize_translation_keys, :en, :'baz.buz', :'foo.bar') - assert_equal [:en, :foo, :bar, :baz, :buz], I18n.send(:normalize_translation_keys, :en, 'baz.buz', 'foo.bar') - assert_equal [:en, :foo, :bar, :baz, :buz], I18n.send(:normalize_translation_keys, :en, %w(baz buz), %w(foo bar)) - assert_equal [:en, :foo, :bar, :baz, :buz], I18n.send(:normalize_translation_keys, :en, [:baz, :buz], [:foo, :bar]) - end - - def test_normalize_keys_should_not_attempt_to_sym_on_empty_string - assert_equal [:en, :foo, :bar, :baz, :buz], I18n.send(:normalize_translation_keys, :en, :'baz.buz', :'foo..bar') - assert_equal [:en, :foo, :bar, :baz, :buz], I18n.send(:normalize_translation_keys, :en, :'baz.buz', :'foo......bar') - end - - def test_uses_passed_separator_to_normalize_keys - assert_equal [:en, :foo, :bar, :baz, :buz], I18n.send(:normalize_translation_keys, :en, :'baz|buz', :'foo|bar', '|') - end - - def test_can_set_exception_handler - assert_nothing_raised { I18n.exception_handler = :custom_exception_handler } - I18n.exception_handler = :default_exception_handler # revert it - end - - with_mocha do - def test_uses_custom_exception_handler - I18n.exception_handler = :custom_exception_handler - I18n.expects(:custom_exception_handler) - I18n.translate :bogus - I18n.exception_handler = :default_exception_handler # revert it - end - - def test_delegates_translate_to_backend - I18n.backend.expects(:translate).with 'de', :foo, {} - I18n.translate :foo, :locale => 'de' - end - - def test_delegates_localize_to_backend - I18n.backend.expects(:localize).with 'de', :whatever, :default - I18n.localize :whatever, :locale => 'de' - end - - def test_translate_given_no_locale_uses_i18n_locale - I18n.backend.expects(:translate).with :en, :foo, {} - I18n.translate :foo - end - end - - def test_translate_on_nested_symbol_keys_works - assert_equal ".", I18n.t(:'currency.format.separator') - end - - def test_translate_with_nested_string_keys_works - assert_equal ".", I18n.t('currency.format.separator') - end - - def test_translate_with_array_as_scope_works - assert_equal ".", I18n.t(:separator, :scope => %w(currency format)) - end - - def test_translate_with_array_containing_dot_separated_strings_as_scope_works - assert_equal ".", I18n.t(:separator, :scope => ['currency.format']) - end - - def test_translate_with_key_array_and_dot_separated_scope_works - assert_equal [".", ","], I18n.t(%w(separator delimiter), :scope => 'currency.format') - end - - def test_translate_with_dot_separated_key_array_and_scope_works - assert_equal [".", ","], I18n.t(%w(format.separator format.delimiter), :scope => 'currency') - end - - with_mocha do - def test_translate_with_options_using_scope_works - I18n.backend.expects(:translate).with('de', :precision, :scope => :"currency.format") - I18n.with_options :locale => 'de', :scope => :'currency.format' do |locale| - locale.t :precision - end - end - end - - # def test_translate_given_no_args_raises_missing_translation_data - # assert_equal "translation missing: en, no key", I18n.t - # end - - def test_translate_given_a_bogus_key_raises_missing_translation_data - assert_equal "translation missing: en, bogus", I18n.t(:bogus) - end - - def test_localize_nil_raises_argument_error - assert_raises(I18n::ArgumentError) { I18n.l nil } - end - - def test_localize_object_raises_argument_error - assert_raises(I18n::ArgumentError) { I18n.l Object.new } - end - - def test_proc_exception_handler - I18n.exception_handler = Proc.new { |exception, locale, key, options| - "No exception here! [Proc handler]" - } - assert_equal "No exception here! [Proc handler]", I18n.translate(:test_proc_handler) - ensure - I18n.exception_handler = :default_exception_handler - end - - def test_class_exception_handler - I18n.exception_handler = Class.new do - def call(exception, locale, key, options) - "No exception here! [Class handler]" - end - end.new - assert_equal "No exception here! [Class handler]", I18n.translate(:test_class_handler) - ensure - I18n.exception_handler = :default_exception_handler - end - -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/fallbacks_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/fallbacks_test.rb deleted file mode 100644 index 573b524..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/fallbacks_test.rb +++ /dev/null @@ -1,126 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../test_helper') - -include I18n::Locale - -class I18nFallbacksDefaultsTest < Test::Unit::TestCase - def teardown - I18n.default_locale = :en - end - - test "defaults reflect the I18n.default_locale if no default has been set manually" do - I18n.default_locale = :'en-US' - fallbacks = Fallbacks.new - assert_equal [:'en-US', :en], fallbacks.defaults - end - - test "defaults reflect a manually passed default locale if any" do - fallbacks = Fallbacks.new(:'fi-FI') - assert_equal [:'fi-FI', :fi], fallbacks.defaults - I18n.default_locale = :'de-DE' - assert_equal [:'fi-FI', :fi], fallbacks.defaults - end - - test "defaults allows to set multiple defaults" do - fallbacks = Fallbacks.new(:'fi-FI', :'se-FI') - assert_equal [:'fi-FI', :fi, :'se-FI', :se], fallbacks.defaults - end -end - -class I18nFallbacksComputationTest < Test::Unit::TestCase - def setup - @fallbacks = Fallbacks.new(:'en-US') - end - - test "with no mappings defined it returns [:es, :en-US] for :es" do - assert_equal [:es, :"en-US", :en], @fallbacks[:es] - end - - test "with no mappings defined it returns [:es-ES, :es, :en-US] for :es-ES" do - assert_equal [:"es-ES", :es, :"en-US", :en], @fallbacks[:"es-ES"] - end - - test "with no mappings defined it returns [:es-MX, :es, :en-US] for :es-MX" do - assert_equal [:"es-MX", :es, :"en-US", :en], @fallbacks[:"es-MX"] - end - - test "with no mappings defined it returns [:es-Latn-ES, :es-Latn, :es, :en-US] for :es-Latn-ES" do - assert_equal [:"es-Latn-ES", :"es-Latn", :es, :"en-US", :en], @fallbacks[:'es-Latn-ES'] - end - - test "with no mappings defined it returns [:en, :en-US] for :en" do - assert_equal [:en, :"en-US"], @fallbacks[:en] - end - - test "with no mappings defined it returns [:en-US, :en] for :en-US (special case: locale == default)" do - assert_equal [:"en-US", :en], @fallbacks[:"en-US"] - end - - # Most people who speak Catalan also live in Spain, so it is safe to assume - # that they also speak Spanish as spoken in Spain. - test "with a Catalan mapping defined it returns [:ca, :es-ES, :es, :en-US] for :ca" do - @fallbacks.map(:ca => :"es-ES") - assert_equal [:ca, :"es-ES", :es, :"en-US", :en], @fallbacks[:ca] - end - - test "with a Catalan mapping defined it returns [:ca-ES, :ca, :es-ES, :es, :en-US] for :ca-ES" do - @fallbacks.map(:ca => :"es-ES") - assert_equal [:"ca-ES", :ca, :"es-ES", :es, :"en-US", :en], @fallbacks[:"ca-ES"] - end - - # People who speak Arabic as spoken in Palestine often times also speak - # Hebrew as spoken in Israel. However it is in no way safe to assume that - # everybody who speaks Arabic also speaks Hebrew. - - test "with a Hebrew mapping defined it returns [:ar, :en-US] for :ar" do - @fallbacks.map(:"ar-PS" => :"he-IL") - assert_equal [:ar, :"en-US", :en], @fallbacks[:ar] - end - - test "with a Hebrew mapping defined it returns [:ar-EG, :ar, :en-US] for :ar-EG" do - @fallbacks.map(:"ar-PS" => :"he-IL") - assert_equal [:"ar-EG", :ar, :"en-US", :en], @fallbacks[:"ar-EG"] - end - - test "with a Hebrew mapping defined it returns [:ar-PS, :ar, :he-IL, :he, :en-US] for :ar-PS" do - @fallbacks.map(:"ar-PS" => :"he-IL") - assert_equal [:"ar-PS", :ar, :"he-IL", :he, :"en-US", :en], @fallbacks[:"ar-PS"] - end - - # Sami people live in several scandinavian countries. In Finnland many people - # know Swedish and Finnish. Thus, it can be assumed that Sami living in - # Finnland also speak Swedish and Finnish. - - test "with a Sami mapping defined it returns [:sms-FI, :sms, :se-FI, :se, :fi-FI, :fi, :en-US] for :sms-FI" do - @fallbacks.map(:sms => [:"se-FI", :"fi-FI"]) - assert_equal [:"sms-FI", :sms, :"se-FI", :se, :"fi-FI", :fi, :"en-US", :en], @fallbacks[:"sms-FI"] - end - - # Austrian people understand German as spoken in Germany - - test "with a German mapping defined it returns [:de, :en-US] for de" do - @fallbacks.map(:"de-AT" => :"de-DE") - assert_equal [:de, :"en-US", :en], @fallbacks[:"de"] - end - - test "with a German mapping defined it returns [:de-DE, :de, :en-US] for de-DE" do - @fallbacks.map(:"de-AT" => :"de-DE") - assert_equal [:"de-DE", :de, :"en-US", :en], @fallbacks[:"de-DE"] - end - - test "with a German mapping defined it returns [:de-AT, :de, :de-DE, :en-US] for de-AT" do - @fallbacks.map(:"de-AT" => :"de-DE") - assert_equal [:"de-AT", :de, :"de-DE", :"en-US", :en], @fallbacks[:"de-AT"] - end - - # Mapping :de => :en, :he => :en - - test "with a mapping :de => :en, :he => :en defined it returns [:de, :en] for :de" do - assert_equal [:de, :"en-US", :en], @fallbacks[:de] - end - - test "with a mapping :de => :en, :he => :en defined it [:he, :en] for :de" do - assert_equal [:he, :"en-US", :en], @fallbacks[:he] - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/tag/rfc4646_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/tag/rfc4646_test.rb deleted file mode 100644 index 3eae4dd..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/tag/rfc4646_test.rb +++ /dev/null @@ -1,143 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../../test_helper') - -class I18nLocaleTagRfc4646ParserTest < Test::Unit::TestCase - include I18n::Locale - - test "Rfc4646::Parser given a valid tag 'de' returns an array of subtags" do - assert_equal ['de', nil, nil, nil, nil, nil, nil], Tag::Rfc4646::Parser.match('de') - end - - test "Rfc4646::Parser given a valid tag 'de' returns an array of subtags" do - assert_equal ['de', nil, 'DE', nil, nil, nil, nil], Tag::Rfc4646::Parser.match('de-DE') - end - - test "Rfc4646::Parser given a valid lowercase tag 'de-latn-de-variant-x-phonebk' returns an array of subtags" do - assert_equal ['de', 'latn', 'de', 'variant', nil, 'x-phonebk', nil], Tag::Rfc4646::Parser.match('de-latn-de-variant-x-phonebk') - end - - test "Rfc4646::Parser given a valid uppercase tag 'DE-LATN-DE-VARIANT-X-PHONEBK' returns an array of subtags" do - assert_equal ['DE', 'LATN', 'DE', 'VARIANT', nil, 'X-PHONEBK', nil], Tag::Rfc4646::Parser.match('DE-LATN-DE-VARIANT-X-PHONEBK') - end - - test "Rfc4646::Parser given an invalid tag 'a-DE' it returns false" do - assert_equal false, Tag::Rfc4646::Parser.match('a-DE') - end - - test "Rfc4646::Parser given an invalid tag 'de-419-DE' it returns false" do - assert_equal false, Tag::Rfc4646::Parser.match('de-419-DE') - end -end - -# Tag for the locale 'de-Latn-DE-Variant-a-ext-x-phonebk-i-klingon' - -class I18nLocaleTagSubtagsTest < Test::Unit::TestCase - include I18n::Locale - - def setup - subtags = %w(de Latn DE variant a-ext x-phonebk i-klingon) - @tag = Tag::Rfc4646.new *subtags - end - - test "returns 'de' as the language subtag in lowercase" do - assert_equal 'de', @tag.language - end - - test "returns 'Latn' as the script subtag in titlecase" do - assert_equal 'Latn', @tag.script - end - - test "returns 'DE' as the region subtag in uppercase" do - assert_equal 'DE', @tag.region - end - - test "returns 'variant' as the variant subtag in lowercase" do - assert_equal 'variant', @tag.variant - end - - test "returns 'a-ext' as the extension subtag" do - assert_equal 'a-ext', @tag.extension - end - - test "returns 'x-phonebk' as the privateuse subtag" do - assert_equal 'x-phonebk', @tag.privateuse - end - - test "returns 'i-klingon' as the grandfathered subtag" do - assert_equal 'i-klingon', @tag.grandfathered - end - - test "returns a formatted tag string from #to_s" do - assert_equal 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon', @tag.to_s - end - - test "returns an array containing the formatted subtags from #to_a" do - assert_equal %w(de Latn DE variant a-ext x-phonebk i-klingon), @tag.to_a - end -end - -# Tag inheritance - -class I18nLocaleTagSubtagsTest < Test::Unit::TestCase - test "#parent returns 'de-Latn-DE-variant-a-ext-x-phonebk' as the parent of 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do - tag = Tag::Rfc4646.new *%w(de Latn DE variant a-ext x-phonebk i-klingon) - assert_equal 'de-Latn-DE-variant-a-ext-x-phonebk', tag.parent.to_s - end - - test "#parent returns 'de-Latn-DE-variant-a-ext' as the parent of 'de-Latn-DE-variant-a-ext-x-phonebk'" do - tag = Tag::Rfc4646.new *%w(de Latn DE variant a-ext x-phonebk) - assert_equal 'de-Latn-DE-variant-a-ext', tag.parent.to_s - end - - test "#parent returns 'de-Latn-DE-variant' as the parent of 'de-Latn-DE-variant-a-ext'" do - tag = Tag::Rfc4646.new *%w(de Latn DE variant a-ext) - assert_equal 'de-Latn-DE-variant', tag.parent.to_s - end - - test "#parent returns 'de-Latn-DE' as the parent of 'de-Latn-DE-variant'" do - tag = Tag::Rfc4646.new *%w(de Latn DE variant) - assert_equal 'de-Latn-DE', tag.parent.to_s - end - - test "#parent returns 'de-Latn' as the parent of 'de-Latn-DE'" do - tag = Tag::Rfc4646.new *%w(de Latn DE) - assert_equal 'de-Latn', tag.parent.to_s - end - - test "#parent returns 'de' as the parent of 'de-Latn'" do - tag = Tag::Rfc4646.new *%w(de Latn) - assert_equal 'de', tag.parent.to_s - end - - # TODO RFC4647 says: "If no language tag matches the request, the "default" value is returned." - # where should we set the default language? - # test "#parent returns '' as the parent of 'de'" do - # tag = Tag::Rfc4646.new *%w(de) - # assert_equal '', tag.parent.to_s - # end - - test "#parent returns an array of 5 parents for 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do - parents = %w(de-Latn-DE-variant-a-ext-x-phonebk-i-klingon - de-Latn-DE-variant-a-ext-x-phonebk - de-Latn-DE-variant-a-ext - de-Latn-DE-variant - de-Latn-DE - de-Latn - de) - tag = Tag::Rfc4646.new *%w(de Latn DE variant a-ext x-phonebk i-klingon) - assert_equal parents, tag.self_and_parents.map{|tag| tag.to_s} - end - - test "returns an array of 5 parents for 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do - parents = %w(de-Latn-DE-variant-a-ext-x-phonebk-i-klingon - de-Latn-DE-variant-a-ext-x-phonebk - de-Latn-DE-variant-a-ext - de-Latn-DE-variant - de-Latn-DE - de-Latn - de) - tag = Tag::Rfc4646.new *%w(de Latn DE variant a-ext x-phonebk i-klingon) - assert_equal parents, tag.self_and_parents.map{|tag| tag.to_s} - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/tag/simple_test.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/tag/simple_test.rb deleted file mode 100644 index b84febe..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/cases/locale/tag/simple_test.rb +++ /dev/null @@ -1,33 +0,0 @@ -# encoding: utf-8 - -require File.expand_path(File.dirname(__FILE__) + '/../../../test_helper') - -class I18nLocaleTagSimpleTest < Test::Unit::TestCase - include I18n::Locale - - test "returns 'de' as the language subtag in lowercase" do - assert_equal %w(de Latn DE), Tag::Simple.new('de-Latn-DE').subtags - end - - test "returns a formatted tag string from #to_s" do - assert_equal 'de-Latn-DE', Tag::Simple.new('de-Latn-DE').to_s - end - - test "returns an array containing the formatted subtags from #to_a" do - assert_equal %w(de Latn DE), Tag::Simple.new('de-Latn-DE').to_a - end - - # Tag inheritance - - test "#parent returns 'de-Latn' as the parent of 'de-Latn-DE'" do - assert_equal 'de-Latn', Tag::Simple.new('de-Latn-DE').parent.to_s - end - - test "#parent returns 'de' as the parent of 'de-Latn'" do - assert_equal 'de', Tag::Simple.new('de-Latn').parent.to_s - end - - test "#self_and_parents returns an array of 3 tags for 'de-Latn-DE'" do - assert_equal %w(de-Latn-DE de-Latn de), Tag::Simple.new('de-Latn-DE').self_and_parents.map { |tag| tag.to_s} - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/de.po b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/de.po deleted file mode 100644 index c162e87..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/de.po +++ /dev/null @@ -1,72 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: version 0.0.1\n" -"POT-Creation-Date: 2009-02-26 19:50+0100\n" -"PO-Revision-Date: 2009-02-18 14:53+0100\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" - -# #: app/helpers/translation_helper.rb:3 -# msgid "%{relative_time} ago" -# msgstr "vor %{relative_time}" - -#: app/views/cars/show.html.erb:5 -msgid "Axis" -msgid_plural "Axis" -msgstr[0] "Achse" -msgstr[1] "Achsen" - -#: app/controllers/cars_controller.rb:47 -msgid "Car was successfully created." -msgstr "Auto wurde erfolgreich gespeichert" - -#: app/controllers/cars_controller.rb:64 -msgid "Car was successfully updated." -msgstr "Auto wurde erfolgreich aktualisiert" - -#: app/views/cars/show.html.erb:1 locale/model_attributes.rb:3 -msgid "Car|Model" -msgstr "Modell" - -#: app/views/cars/show.html.erb:3 locale/model_attributes.rb:4 -msgid "Car|Wheels count" -msgstr "Räderzahl" - -#: app/views/cars/show.html.erb:7 -msgid "Created" -msgstr "Erstellt" - -#: app/views/cars/show.html.erb:9 -msgid "Month" -msgstr "Monat" - -#: locale/model_attributes.rb:2 -msgid "car" -msgstr "Auto" - -#: locale/testlog_phrases.rb:2 -msgid "this is a dynamic translation which was found thorugh gettext_test_log!" -msgstr "" -"Dies ist eine dynamische Übersetzung, die durch gettext_test_log " -"gefunden wurde!" - -#: app/views/cars/nowhere_really -msgid "Car|wheel" -msgid_plural "Car|wheels" -msgstr[0] "Rad" -msgstr[1] "Räder" - -msgid "On %{count} wheel." -msgid_plural "On %{count} wheels." -msgstr[0] "Auf %{count} Achse." -msgstr[1] "Auf %{count} Achsen." diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/en.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/en.rb deleted file mode 100644 index e847d10..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/en.rb +++ /dev/null @@ -1,3 +0,0 @@ -# encoding: utf-8 - -{ :en => { :fuh => { :bah => "bas" } } } \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/en.yml b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/en.yml deleted file mode 100644 index 25f5bb6..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/en.yml +++ /dev/null @@ -1,3 +0,0 @@ -en: - foo: - bar: baz \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/plurals.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/plurals.rb deleted file mode 100644 index d1ea34e..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/fixtures/locales/plurals.rb +++ /dev/null @@ -1,113 +0,0 @@ -# encoding: utf-8 - -{ - :af => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :am => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :ar => { :i18n => { :plural => { :keys => [:zero, :one, :two, :few, :many, :other], :rule => lambda { |n| n == 0 ? :zero : n == 1 ? :one : n == 2 ? :two : [3, 4, 5, 6, 7, 8, 9, 10].include?(n % 100) ? :few : [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99].include?(n % 100) ? :many : :other } } } }, - :az => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :be => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, - :bg => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :bh => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :bn => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :bo => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :bs => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, - :ca => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :cs => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n) ? :few : :other } } } }, - :cy => { :i18n => { :plural => { :keys => [:one, :two, :many, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : n == 8 || n == 11 ? :many : :other } } } }, - :da => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :de => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :dz => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :el => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :en => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :eo => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :es => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :et => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :eu => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :fa => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :fi => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :fil => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :fo => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :fr => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n && n != 2 ? :one : :other } } } }, - :fur => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :fy => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :ga => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, - :gl => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :gu => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :guw => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :ha => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :he => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :hi => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :hr => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, - :hu => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :id => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :is => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :it => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :iw => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :ja => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :jv => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :ka => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :km => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :kn => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :ko => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :ku => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :lb => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :ln => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :lt => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n % 10 == 1 && ![11, 12, 13, 14, 15, 16, 17, 18, 19].include?(n % 100) ? :one : [2, 3, 4, 5, 6, 7, 8, 9].include?(n % 10) && ![11, 12, 13, 14, 15, 16, 17, 18, 19].include?(n % 100) ? :few : :other } } } }, - :lv => { :i18n => { :plural => { :keys => [:zero, :one, :other], :rule => lambda { |n| n == 0 ? :zero : n % 10 == 1 && n % 100 != 11 ? :one : :other } } } }, - :mg => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :mk => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n % 10 == 1 ? :one : :other } } } }, - :ml => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :mn => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :mo => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : n == 0 ? :few : :other } } } }, - :mr => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :ms => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :mt => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n == 1 ? :one : n == 0 || [2, 3, 4, 5, 6, 7, 8, 9, 10].include?(n % 100) ? :few : [11, 12, 13, 14, 15, 16, 17, 18, 19].include?(n % 100) ? :many : :other } } } }, - :my => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :nah => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :nb => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :ne => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :nl => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :nn => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :no => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :nso => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :om => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :or => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :pa => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :pap => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :pl => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) && ![22, 23, 24].include?(n % 100) ? :few : :other } } } }, - :ps => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :pt => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :"pt-PT" => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :ro => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : n == 0 ? :few : :other } } } }, - :ru => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, - :se => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, - :sh => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, - :sk => { :i18n => { :plural => { :keys => [:one, :few, :other], :rule => lambda { |n| n == 1 ? :one : [2, 3, 4].include?(n) ? :few : :other } } } }, - :sl => { :i18n => { :plural => { :keys => [:one, :two, :few, :other], :rule => lambda { |n| n % 100 == 1 ? :one : n % 100 == 2 ? :two : [3, 4].include?(n % 100) ? :few : :other } } } }, - :sma => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, - :smi => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, - :smj => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, - :smn => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, - :sms => { :i18n => { :plural => { :keys => [:one, :two, :other], :rule => lambda { |n| n == 1 ? :one : n == 2 ? :two : :other } } } }, - :so => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :sq => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :sr => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, - :sv => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :sw => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :ta => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :te => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :th => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :ti => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :tk => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :tl => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :to => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :tr => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :uk => { :i18n => { :plural => { :keys => [:one, :few, :many, :other], :rule => lambda { |n| n % 10 == 1 && n % 100 != 11 ? :one : [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100) ? :few : n % 10 == 0 || [5, 6, 7, 8, 9].include?(n % 10) || [11, 12, 13, 14].include?(n % 100) ? :many : :other } } } }, - :ur => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } }, - :vi => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :wa => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| [0, 1].include?(n) ? :one : :other } } } }, - :yo => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :zh => { :i18n => { :plural => { :keys => [:other], :rule => lambda { |n| :other } } } }, - :zu => { :i18n => { :plural => { :keys => [:one, :other], :rule => lambda { |n| n == 1 ? :one : :other } } } } -} - diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/test_helper.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/test/test_helper.rb deleted file mode 100644 index 91abe67..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/test/test_helper.rb +++ /dev/null @@ -1,100 +0,0 @@ -# encoding: utf-8 - -$:.unshift File.expand_path("../lib", File.dirname(__FILE__)) -$:.unshift File.expand_path(File.dirname(__FILE__)) - -require 'i18n' -require 'i18n/core_ext/object/meta_class' - -require 'rubygems' -require 'test/unit' -require 'time' -require 'yaml' - -begin - require 'mocha' -rescue LoadError - puts "skipping tests using mocha as mocha can't be found" -end - - -Dir[File.dirname(__FILE__) + '/api/**/*.rb'].each do |filename| - require filename -end - -$KCODE = 'u' unless RUBY_VERSION >= '1.9' - -# wtf is wrong with this, why's there Kernel#test? -# class Module -# def self.test(name, &block) -# define_method("test: " + name, &block) -# end -# end - -class Test::Unit::TestCase - def self.test(name, &block) - define_method("test: " + name, &block) - end - - def self.with_mocha - yield if Object.respond_to?(:expects) - end - - def teardown - I18n.locale = nil - I18n.default_locale = :en - I18n.load_path = [] - I18n.available_locales = nil - I18n.backend = nil - end - - def translations - I18n.backend.instance_variable_get(:@translations) - end - - def store_translations(*args) - data = args.pop - locale = args.pop || :en - I18n.backend.store_translations(locale, data) - end - - def locales_dir - File.dirname(__FILE__) + '/fixtures/locales' - end - - def euc_jp(string) - string.encode!(Encoding::EUC_JP) - end - - def can_store_procs? - I18n::Backend::ActiveRecord === I18n.backend and - I18n::Backend::ActiveRecord.included_modules.include?(I18n::Backend::ActiveRecord::StoreProcs) - end -end - -def setup_active_record - begin - require 'activerecord' - require 'i18n/backend/active_record' - require 'i18n/backend/active_record/store_procs' - - if I18n::Backend::Simple.method_defined?(:interpolate_with_deprecated_syntax) - I18n::Backend::Simple.send(:remove_method, :interpolate) rescue NameError - end - - ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:") - ActiveRecord::Migration.verbose = false - ActiveRecord::Schema.define(:version => 1) do - create_table :translations do |t| - t.string :locale - t.string :key - t.string :value - t.string :interpolations - t.boolean :is_proc, :default => false - end - end - - rescue LoadError - puts "skipping tests using activerecord as activerecord can't be found" - end -end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.3/vendor/po_parser.rb b/activesupport/lib/active_support/vendor/i18n-0.3.3/vendor/po_parser.rb deleted file mode 100644 index 547df6a..0000000 --- a/activesupport/lib/active_support/vendor/i18n-0.3.3/vendor/po_parser.rb +++ /dev/null @@ -1,329 +0,0 @@ -=begin - poparser.rb - Generate a .mo - - Copyright (C) 2003-2009 Masao Mutoh - - You may redistribute it and/or modify it under the same - license terms as Ruby. -=end - -#MODIFIED -# removed include GetText etc -# added stub translation method _(x) -require 'racc/parser' - -module GetText - - class PoParser < Racc::Parser - - def _(x) - x - end - -module_eval <<'..end src/poparser.ry modeval..id7a99570e05', 'src/poparser.ry', 108 - def unescape(orig) - ret = orig.gsub(/\\n/, "\n") - ret.gsub!(/\\t/, "\t") - ret.gsub!(/\\r/, "\r") - ret.gsub!(/\\"/, "\"") - ret - end - - def parse(str, data, ignore_fuzzy = true) - @comments = [] - @data = data - @fuzzy = false - @msgctxt = "" - $ignore_fuzzy = ignore_fuzzy - - str.strip! - @q = [] - until str.empty? do - case str - when /\A\s+/ - str = $' - when /\Amsgctxt/ - @q.push [:MSGCTXT, $&] - str = $' - when /\Amsgid_plural/ - @q.push [:MSGID_PLURAL, $&] - str = $' - when /\Amsgid/ - @q.push [:MSGID, $&] - str = $' - when /\Amsgstr/ - @q.push [:MSGSTR, $&] - str = $' - when /\A\[(\d+)\]/ - @q.push [:PLURAL_NUM, $1] - str = $' - when /\A\#~(.*)/ - $stderr.print _("Warning: obsolete msgid exists.\n") - $stderr.print " #{$&}\n" - @q.push [:COMMENT, $&] - str = $' - when /\A\#(.*)/ - @q.push [:COMMENT, $&] - str = $' - when /\A\"(.*)\"/ - @q.push [:STRING, $1] - str = $' - else - #c = str[0,1] - #@q.push [:STRING, c] - str = str[1..-1] - end - end - @q.push [false, '$end'] - if $DEBUG - @q.each do |a,b| - puts "[#{a}, #{b}]" - end - end - @yydebug = true if $DEBUG - do_parse - - if @comments.size > 0 - @data.set_comment(:last, @comments.join("\n")) - end - @data - end - - def next_token - @q.shift - end - - def on_message(msgid, msgstr) - if msgstr.size > 0 - @data[msgid] = msgstr - @data.set_comment(msgid, @comments.join("\n")) - end - @comments.clear - @msgctxt = "" - end - - def on_comment(comment) - @fuzzy = true if (/fuzzy/ =~ comment) - @comments << comment - end - - -..end src/poparser.ry modeval..id7a99570e05 - -##### racc 1.4.5 generates ### - -racc_reduce_table = [ - 0, 0, :racc_error, - 0, 10, :_reduce_none, - 2, 10, :_reduce_none, - 2, 10, :_reduce_none, - 2, 10, :_reduce_none, - 2, 12, :_reduce_5, - 1, 13, :_reduce_none, - 1, 13, :_reduce_none, - 4, 15, :_reduce_8, - 5, 16, :_reduce_9, - 2, 17, :_reduce_10, - 1, 17, :_reduce_none, - 3, 18, :_reduce_12, - 1, 11, :_reduce_13, - 2, 14, :_reduce_14, - 1, 14, :_reduce_15 ] - -racc_reduce_n = 16 - -racc_shift_n = 26 - -racc_action_table = [ - 3, 13, 5, 7, 9, 15, 16, 17, 20, 17, - 13, 17, 13, 13, 11, 17, 23, 20, 13, 17 ] - -racc_action_check = [ - 1, 16, 1, 1, 1, 12, 12, 12, 18, 18, - 7, 14, 15, 9, 3, 19, 20, 21, 23, 25 ] - -racc_action_pointer = [ - nil, 0, nil, 14, nil, nil, nil, 3, nil, 6, - nil, nil, 0, nil, 4, 5, -6, nil, 2, 8, - 8, 11, nil, 11, nil, 12 ] - -racc_action_default = [ - -1, -16, -2, -16, -3, -13, -4, -16, -6, -16, - -7, 26, -16, -15, -5, -16, -16, -14, -16, -8, - -16, -9, -11, -16, -10, -12 ] - -racc_goto_table = [ - 12, 22, 14, 4, 24, 6, 2, 8, 18, 19, - 10, 21, 1, nil, nil, nil, 25 ] - -racc_goto_check = [ - 5, 9, 5, 3, 9, 4, 2, 6, 5, 5, - 7, 8, 1, nil, nil, nil, 5 ] - -racc_goto_pointer = [ - nil, 12, 5, 2, 4, -7, 6, 9, -7, -17 ] - -racc_goto_default = [ - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil ] - -racc_token_table = { - false => 0, - Object.new => 1, - :COMMENT => 2, - :MSGID => 3, - :MSGCTXT => 4, - :MSGID_PLURAL => 5, - :MSGSTR => 6, - :STRING => 7, - :PLURAL_NUM => 8 } - -racc_use_result_var = true - -racc_nt_base = 9 - -Racc_arg = [ - racc_action_table, - racc_action_check, - racc_action_default, - racc_action_pointer, - racc_goto_table, - racc_goto_check, - racc_goto_default, - racc_goto_pointer, - racc_nt_base, - racc_reduce_table, - racc_token_table, - racc_shift_n, - racc_reduce_n, - racc_use_result_var ] - -Racc_token_to_s_table = [ -'$end', -'error', -'COMMENT', -'MSGID', -'MSGCTXT', -'MSGID_PLURAL', -'MSGSTR', -'STRING', -'PLURAL_NUM', -'$start', -'msgfmt', -'comment', -'msgctxt', -'message', -'string_list', -'single_message', -'plural_message', -'msgstr_plural', -'msgstr_plural_line'] - -Racc_debug_parser = true - -##### racc system variables end ##### - - # reduce 0 omitted - - # reduce 1 omitted - - # reduce 2 omitted - - # reduce 3 omitted - - # reduce 4 omitted - -module_eval <<'.,.,', 'src/poparser.ry', 25 - def _reduce_5( val, _values, result ) - @msgctxt = unescape(val[1]) + "\004" - result - end -.,., - - # reduce 6 omitted - - # reduce 7 omitted - -module_eval <<'.,.,', 'src/poparser.ry', 48 - def _reduce_8( val, _values, result ) - if @fuzzy and $ignore_fuzzy - if val[1] != "" - $stderr.print _("Warning: fuzzy message was ignored.\n") - $stderr.print " msgid '#{val[1]}'\n" - else - on_message('', unescape(val[3])) - end - @fuzzy = false - else - on_message(@msgctxt + unescape(val[1]), unescape(val[3])) - end - result = "" - result - end -.,., - -module_eval <<'.,.,', 'src/poparser.ry', 65 - def _reduce_9( val, _values, result ) - if @fuzzy and $ignore_fuzzy - if val[1] != "" - $stderr.print _("Warning: fuzzy message was ignored.\n") - $stderr.print "msgid = '#{val[1]}\n" - else - on_message('', unescape(val[3])) - end - @fuzzy = false - else - on_message(@msgctxt + unescape(val[1]) + "\000" + unescape(val[3]), unescape(val[4])) - end - result = "" - result - end -.,., - -module_eval <<'.,.,', 'src/poparser.ry', 76 - def _reduce_10( val, _values, result ) - if val[0].size > 0 - result = val[0] + "\000" + val[1] - else - result = "" - end - result - end -.,., - - # reduce 11 omitted - -module_eval <<'.,.,', 'src/poparser.ry', 84 - def _reduce_12( val, _values, result ) - result = val[2] - result - end -.,., - -module_eval <<'.,.,', 'src/poparser.ry', 91 - def _reduce_13( val, _values, result ) - on_comment(val[0]) - result - end -.,., - -module_eval <<'.,.,', 'src/poparser.ry', 99 - def _reduce_14( val, _values, result ) - result = val.delete_if{|item| item == ""}.join - result - end -.,., - -module_eval <<'.,.,', 'src/poparser.ry', 103 - def _reduce_15( val, _values, result ) - result = val[0] - result - end -.,., - - def _reduce_none( val, _values, result ) - result - end - - end # class PoParser - -end # module GetText diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n.rb new file mode 100644 index 0000000..7edde69 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n.rb @@ -0,0 +1,335 @@ +# encoding: utf-8 + +# Authors:: Sven Fuchs (http://www.artweb-design.de), +# Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey), +# Stephan Soller (http://www.arkanis-development.de/), +# Saimon Moore (http://saimonmoore.net), +# Matt Aimonetti (http://railsontherun.com/) +# Copyright:: Copyright (c) 2008 The Ruby i18n Team +# License:: MIT +require 'i18n/exceptions' +require 'i18n/core_ext/string/interpolate' + +module I18n + autoload :Backend, 'i18n/backend' + autoload :Helpers, 'i18n/helpers' + autoload :Locale, 'i18n/locale' + + class Config + # The only configuration value that is not global and scoped to thread is :locale. + # It defaults to the default_locale. + def locale + @locale ||= default_locale + end + + # Sets the current locale pseudo-globally, i.e. in the Thread.current hash. + def locale=(locale) + @locale = locale.to_sym rescue nil + end + + # Returns the current backend. Defaults to +Backend::Simple+. + def backend + @@backend ||= Backend::Simple.new + end + + # Sets the current backend. Used to set a custom backend. + def backend=(backend) + @@backend = backend + end + + # Returns the current default locale. Defaults to :'en' + def default_locale + @@default_locale ||= :en + end + + # Sets the current default locale. Used to set a custom default locale. + def default_locale=(locale) + @@default_locale = locale.to_sym rescue nil + end + + # Returns an array of locales for which translations are available. + # Unless you explicitely set the these through I18n.available_locales= + # the call will be delegated to the backend and memoized on the I18n module. + def available_locales + @@available_locales ||= backend.available_locales + end + + # Sets the available locales. + def available_locales=(locales) + @@available_locales = locales + end + + # Returns the current default scope separator. Defaults to '.' + def default_separator + @@default_separator ||= '.' + end + + # Sets the current default scope separator. + def default_separator=(separator) + @@default_separator = separator + end + + # Return the current exception handler. Defaults to :default_exception_handler. + def exception_handler + @@exception_handler ||= :default_exception_handler + end + + # Sets the exception handler. + def exception_handler=(exception_handler) + @@exception_handler = exception_handler + end + + # Allow clients to register paths providing translation data sources. The + # backend defines acceptable sources. + # + # E.g. the provided SimpleBackend accepts a list of paths to translation + # files which are either named *.rb and contain plain Ruby Hashes or are + # named *.yml and contain YAML data. So for the SimpleBackend clients may + # register translation files like this: + # I18n.load_path << 'path/to/locale/en.yml' + def load_path + @@load_path ||= [] + end + + # Sets the load path instance. Custom implementations are expected to + # behave like a Ruby Array. + def load_path=(load_path) + @@load_path = load_path + end + end + + class << self + + # Gets I18n configuration object. + def config + Thread.current[:i18n_config] ||= I18n::Config.new + end + + # Sets I18n configuration object. + def config=(value) + Thread.current[:i18n_config] = value + end + + # Write methods which delegates to the configuration object + %w(locale backend default_locale available_locales default_separator + exception_handler load_path).each do |method| + module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1 + def #{method} + config.#{method} + end + + def #{method}=(value) + config.#{method} = (value) + end + DELEGATORS + end + + # Tells the backend to reload translations. Used in situations like the + # Rails development environment. Backends can implement whatever strategy + # is useful. + def reload! + config.backend.reload! + end + + # Translates, pluralizes and interpolates a given key using a given locale, + # scope, and default, as well as interpolation values. + # + # *LOOKUP* + # + # Translation data is organized as a nested hash using the upper-level keys + # as namespaces. E.g., ActionView ships with the translation: + # :date => {:formats => {:short => "%b %d"}}. + # + # Translations can be looked up at any level of this hash using the key argument + # and the scope option. E.g., in this example I18n.t :date + # returns the whole translations hash {:formats => {:short => "%b %d"}}. + # + # Key can be either a single key or a dot-separated key (both Strings and Symbols + # work). E.g., the short format can be looked up using both: + # I18n.t 'date.formats.short' + # I18n.t :'date.formats.short' + # + # Scope can be either a single key, a dot-separated key or an array of keys + # or dot-separated keys. Keys and scopes can be combined freely. So these + # examples will all look up the same short date format: + # I18n.t 'date.formats.short' + # I18n.t 'formats.short', :scope => 'date' + # I18n.t 'short', :scope => 'date.formats' + # I18n.t 'short', :scope => %w(date formats) + # + # *INTERPOLATION* + # + # Translations can contain interpolation variables which will be replaced by + # values passed to #translate as part of the options hash, with the keys matching + # the interpolation variable names. + # + # E.g., with a translation :foo => "foo {{bar}}" the option + # value for the key +bar+ will be interpolated into the translation: + # I18n.t :foo, :bar => 'baz' # => 'foo baz' + # + # *PLURALIZATION* + # + # Translation data can contain pluralized translations. Pluralized translations + # are arrays of singluar/plural versions of translations like ['Foo', 'Foos']. + # + # Note that I18n::Backend::Simple only supports an algorithm for English + # pluralization rules. Other algorithms can be supported by custom backends. + # + # This returns the singular version of a pluralized translation: + # I18n.t :foo, :count => 1 # => 'Foo' + # + # These both return the plural version of a pluralized translation: + # I18n.t :foo, :count => 0 # => 'Foos' + # I18n.t :foo, :count => 2 # => 'Foos' + # + # The :count option can be used both for pluralization and interpolation. + # E.g., with the translation + # :foo => ['{{count}} foo', '{{count}} foos'], count will + # be interpolated to the pluralized translation: + # I18n.t :foo, :count => 1 # => '1 foo' + # + # *DEFAULTS* + # + # This returns the translation for :foo or default if no translation was found: + # I18n.t :foo, :default => 'default' + # + # This returns the translation for :foo or the translation for :bar if no + # translation for :foo was found: + # I18n.t :foo, :default => :bar + # + # Returns the translation for :foo or the translation for :bar + # or default if no translations for :foo and :bar were found. + # I18n.t :foo, :default => [:bar, 'default'] + # + # *BULK LOOKUP* + # + # This returns an array with the translations for :foo and :bar. + # I18n.t [:foo, :bar] + # + # Can be used with dot-separated nested keys: + # I18n.t [:'baz.foo', :'baz.bar'] + # + # Which is the same as using a scope option: + # I18n.t [:foo, :bar], :scope => :baz + # + # *LAMBDAS* + # + # Both translations and defaults can be given as Ruby lambdas. Lambdas will be + # called and passed the key and options. + # + # E.g. assuming the key :salutation resolves to: + # lambda { |key, options| options[:gender] == 'm' ? "Mr. {{options[:name]}}" : "Mrs. {{options[:name]}}" } + # + # Then I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith". + # + # It is recommended to use/implement lambdas in an "idempotent" way. E.g. when + # a cache layer is put in front of I18n.translate it will generate a cache key + # from the argument values passed to #translate. Therefor your lambdas should + # always return the same translations/values per unique combination of argument + # values. + def translate(*args) + options = args.pop if args.last.is_a?(Hash) + key = args.shift + locale = options && options.delete(:locale) || config.locale + raises = options && options.delete(:raise) + config.backend.translate(locale, key, options || {}) + rescue I18n::ArgumentError => exception + raise exception if raises + handle_exception(exception, locale, key, options) + end + alias :t :translate + + def translate!(key, options = {}) + translate(key, options.merge( :raise => true )) + end + alias :t! :translate! + + # Localizes certain objects, such as dates and numbers to local formatting. + def localize(object, options = {}) + locale = options.delete(:locale) || config.locale + format = options.delete(:format) || :default + config.backend.localize(locale, object, format, options) + end + alias :l :localize + + # Merges the given locale, key and scope into a single array of keys. + # Splits keys that contain dots into multiple keys. Makes sure all + # keys are Symbols. + def normalize_keys(locale, key, scope, separator = nil) + separator ||= I18n.default_separator + normalize_key(locale, separator) + + normalize_key(scope, separator) + + normalize_key(key, separator) + end + + # making these private until Ruby 1.9.2 can send to protected methods again + # see http://redmine.ruby-lang.org/repositories/revision/ruby-19?rev=24280 + private + + # Handles exceptions raised in the backend. All exceptions except for + # MissingTranslationData exceptions are re-raised. When a MissingTranslationData + # was caught and the option :raise is not set the handler returns an error + # message string containing the key/scope. + def default_exception_handler(exception, locale, key, options) + return exception.message if MissingTranslationData === exception + raise exception + end + + # Any exceptions thrown in translate will be sent to the @@exception_handler + # which can be a Symbol, a Proc or any other Object. + # + # If exception_handler is a Symbol then it will simply be sent to I18n as + # a method call. A Proc will simply be called. In any other case the + # method #call will be called on the exception_handler object. + # + # Examples: + # + # I18n.exception_handler = :default_exception_handler # this is the default + # I18n.default_exception_handler(exception, locale, key, options) # will be called like this + # + # I18n.exception_handler = lambda { |*args| ... } # a lambda + # I18n.exception_handler.call(exception, locale, key, options) # will be called like this + # + # I18n.exception_handler = I18nExceptionHandler.new # an object + # I18n.exception_handler.call(exception, locale, key, options) # will be called like this + def handle_exception(exception, locale, key, options) + case config.exception_handler + when Symbol + send(config.exception_handler, exception, locale, key, options) + else + config.exception_handler.call(exception, locale, key, options) + end + end + + # Deprecated. Will raise a warning in future versions and then finally be + # removed. Use I18n.normalize_keys instead. + def normalize_translation_keys(locale, key, scope, separator = nil) + normalize_keys(locale, key, scope, separator) + end + + def normalize_key(key, separator) + normalized_key_cache(separator)[key] ||= + case key + when Array + key.map { |k| normalize_key(k, separator) }.flatten + when nil + [] + else + key = key.to_s + if key == '' + [] + elsif key.include?(separator) + keys = key.split(separator) - [''] + keys.map { |k| k.to_sym } + else + [key.to_sym] + end + end + end + + def normalized_key_cache(separator) + @normalized_key_cache ||= Hash.new { |h,k| h[k] = {} } + @normalized_key_cache[separator] + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend.rb new file mode 100644 index 0000000..964af24 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend.rb @@ -0,0 +1,19 @@ +module I18n + module Backend + autoload :ActiveRecord, 'i18n/backend/active_record' + autoload :Base, 'i18n/backend/base' + autoload :Cache, 'i18n/backend/cache' + autoload :Cascade, 'i18n/backend/cascade' + autoload :Chain, 'i18n/backend/chain' + autoload :Cldr, 'i18n/backend/cldr' + autoload :Fallbacks, 'i18n/backend/fallbacks' + autoload :Fast, 'i18n/backend/fast' + autoload :Gettext, 'i18n/backend/gettext' + autoload :Helpers, 'i18n/backend/helpers' + autoload :Links, 'i18n/backend/links' + autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler' + autoload :Metadata, 'i18n/backend/metadata' + autoload :Pluralization, 'i18n/backend/pluralization' + autoload :Simple, 'i18n/backend/simple' + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record.rb new file mode 100644 index 0000000..19e1777 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record.rb @@ -0,0 +1,66 @@ +require 'i18n/backend/base' +require 'i18n/backend/active_record/translation' + +module I18n + module Backend + class ActiveRecord + autoload :Missing, 'i18n/backend/active_record/missing' + autoload :StoreProcs, 'i18n/backend/active_record/store_procs' + autoload :Translation, 'i18n/backend/active_record/translation' + + include Base, Links + + def reload! + end + + def store_translations(locale, data, options = {}) + separator = options[:separator] || I18n.default_separator + wind_keys(data, separator).each do |key, value| + store_link(locale, key, value) if value.is_a?(Symbol) + Translation.locale(locale).lookup(expand_keys(key, separator), separator).delete_all + Translation.create(:locale => locale.to_s, :key => key.to_s, :value => value) + end + end + + def available_locales + begin + Translation.available_locales + rescue ::ActiveRecord::StatementInvalid + [] + end + end + + protected + + def lookup(locale, key, scope = [], options = {}) + return unless key + + separator = options[:separator] || I18n.default_separator + + key = resolve_link(locale, key) + key = (Array(scope) + Array(key)).join(separator) + result = Translation.locale(locale).lookup(key, separator).all + + if result.empty? + return nil + elsif result.first.key == key + return result.first.value + else + chop_range = (key.size + separator.size)..-1 + result = result.inject({}) do |hash, r| + hash[r.key.slice(chop_range)] = r.value + hash + end + deep_symbolize_keys(unwind_keys(result, separator)) + end + end + + # For a key :'foo.bar.baz' return ['foo', 'foo.bar', 'foo.bar.baz'] + def expand_keys(key, separator = I18n.default_separator) + key.to_s.split(separator).inject([]) do |keys, key| + keys << [keys.last, key].compact.join(separator) + end + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/missing.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/missing.rb new file mode 100644 index 0000000..ad56df7 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/missing.rb @@ -0,0 +1,67 @@ +# This extension stores translation stub records for missing translations to +# the database. +# +# This is useful if you have a web based translation tool. It will populate +# the database with untranslated keys as the application is being used. A +# translator can then go through these and add missing translations. +# +# Example usage: +# +# I18n::Backend::Chain.send(:include, I18n::Backend::ActiveRecord::Missing) +# I18n.backend = I18nChainBackend.new(I18n::Backend::ActiveRecord.new, I18n::Backend::Simple.new) +# +# Stub records for pluralizations will also be created for each key defined +# in i18n.plural.keys. +# +# For example: +# +# # en.yml +# en: +# i18n: +# plural: +# keys: [:zero, :one, :other] +# +# # pl.yml +# pl: +# i18n: +# plural: +# keys: [:zero, :one, :few, :other] +# +# It will also persist interpolation keys in Translation#interpolations so +# translators will be able to review and use them. +module I18n + module Backend + class ActiveRecord + module Missing + def store_default_translations(locale, key, options = {}) + count, scope, default, separator = options.values_at(:count, *Base::RESERVED_KEYS) + separator ||= I18n.default_separator + + keys = I18n.normalize_keys(locale, key, scope, separator)[1..-1] + key = keys.join(separator || I18n.default_separator) + + unless ActiveRecord::Translation.locale(locale).lookup(key, separator).exists? + interpolations = options.reject { |name, value| Base::RESERVED_KEYS.include?(name) }.keys + keys = count ? I18n.t('i18n.plural.keys', :locale => locale).map { |k| [key, k].join(separator) } : [key] + keys.each { |key| store_default_translation(locale, key, interpolations) } + end + end + + def store_default_translation(locale, key, interpolations) + translation = ActiveRecord::Translation.new :locale => locale.to_s, :key => key + translation.interpolations = interpolations + translation.save + end + + def translate(locale, key, options = {}) + super + + rescue I18n::MissingTranslationData => e + self.store_default_translations(locale, key, options) + + raise e + end + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/store_procs.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/store_procs.rb new file mode 100644 index 0000000..652b1aa --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/store_procs.rb @@ -0,0 +1,38 @@ +# This module is intended to be mixed into the ActiveRecord backend to allow +# storing Ruby Procs as translation values in the database. +# +# I18n.backend = I18n::Backend::ActiveRecord.new +# I18n::Backend::ActiveRecord::Translation.send(:include, I18n::Backend::ActiveRecord::StoreProcs) +# +# The StoreProcs module requires the ParseTree and ruby2ruby gems and therefor +# was extracted from the original backend. +# +# ParseTree is not compatible with Ruby 1.9. + +begin + require 'ruby2ruby' + require 'parse_tree' + require 'parse_tree_extensions' +rescue LoadError => e + puts "can't use StoreProcs because: #{e.message}" +end + +module I18n + module Backend + class ActiveRecord + module StoreProcs + def value=(v) + case v + when Proc + write_attribute(:value, v.to_ruby) + write_attribute(:is_proc, true) + else + write_attribute(:value, v) + end + end + + Translation.send(:include, self) if method(:to_s).respond_to?(:to_ruby) + end + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/translation.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/translation.rb new file mode 100644 index 0000000..f3d3556 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/active_record/translation.rb @@ -0,0 +1,88 @@ +require 'active_record' + +module I18n + module Backend + # ActiveRecord model used to store actual translations to the database. + # + # This model expects a table like the following to be already set up in + # your the database: + # + # create_table :translations do |t| + # t.string :locale + # t.string :key + # t.text :value + # t.text :interpolations + # t.boolean :is_proc, :default => false + # end + # + # This model supports to named scopes :locale and :lookup. The :locale + # scope simply adds a condition for a given locale: + # + # I18n::Backend::ActiveRecord::Translation.locale(:en).all + # # => all translation records that belong to the :en locale + # + # The :lookup scope adds a condition for looking up all translations + # that either start with the given keys (joined by an optionally given + # separator or I18n.default_separator) or that exactly have this key. + # + # # with translations present for :"foo.bar" and :"foo.baz" + # I18n::Backend::ActiveRecord::Translation.lookup(:foo) + # # => an array with both translation records :"foo.bar" and :"foo.baz" + # + # I18n::Backend::ActiveRecord::Translation.lookup([:foo, :bar]) + # I18n::Backend::ActiveRecord::Translation.lookup(:"foo.bar") + # # => an array with the translation record :"foo.bar" + # + # When the StoreProcs module was mixed into this model then Procs will + # be stored to the database as Ruby code and evaluated when :value is + # called. + # + # Translation = I18n::Backend::ActiveRecord::Translation + # Translation.create \ + # :locale => 'en' + # :key => 'foo' + # :value => lambda { |key, options| 'FOO' } + # Translation.find_by_locale_and_key('en', 'foo').value + # # => 'FOO' + class ActiveRecord + class Translation < ::ActiveRecord::Base + set_table_name 'translations' + attr_protected :is_proc, :interpolations + + serialize :value + serialize :interpolations, Array + + scope_method = ::ActiveRecord::VERSION::MAJOR == 2 ? :named_scope : :scope + + send scope_method, :locale, lambda { |locale| + { :conditions => { :locale => locale.to_s } } + } + + send scope_method, :lookup, lambda { |keys, *separator| + column_name = connection.quote_column_name('key') + keys = Array(keys).map! { |key| key.to_s } + separator = separator.first || I18n.default_separator + namespace = "#{keys.last}#{separator}%" + { :conditions => ["#{column_name} IN (?) OR #{column_name} LIKE ?", keys, namespace] } + } + + def self.available_locales + Translation.find(:all, :select => 'DISTINCT locale').map { |t| t.locale.to_sym } + end + + def interpolates?(key) + self.interpolations.include?(key) if self.interpolations + end + + def value + if is_proc + Kernel.eval(read_attribute(:value)) + else + value = read_attribute(:value) + value == 'f' ? false : value + end + end + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/base.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/base.rb new file mode 100644 index 0000000..a569271 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/base.rb @@ -0,0 +1,266 @@ +# encoding: utf-8 + +require 'yaml' +require 'i18n/core_ext/hash/except' + +module I18n + module Backend + module Base + include I18n::Backend::Helpers + + RESERVED_KEYS = [:scope, :default, :separator, :resolve] + INTERPOLATION_SYNTAX_PATTERN = /(\\)?\{\{([^\}]+)\}\}/ + + # Accepts a list of paths to translation files. Loads translations from + # plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml + # for details. + def load_translations(*filenames) + filenames.each { |filename| load_file(filename) } + end + + # Stores translations for the given locale in memory. + # This uses a deep merge for the translations hash, so existing + # translations will be overwritten by new ones only at the deepest + # level of the hash. + def store_translations(locale, data, options = {}) + merge_translations(locale, data, options) + end + + def translate(locale, key, options = {}) + raise InvalidLocale.new(locale) unless locale + return key.map { |k| translate(locale, k, options) } if key.is_a?(Array) + + if options.empty? + entry = resolve(locale, key, lookup(locale, key), options) + raise(I18n::MissingTranslationData.new(locale, key, options)) if entry.nil? + else + count, scope, default = options.values_at(:count, :scope, :default) + values = options.reject { |name, value| RESERVED_KEYS.include?(name) } + + entry = lookup(locale, key, scope, options) + entry = entry.nil? && default ? default(locale, key, default, options) : resolve(locale, key, entry, options) + raise(I18n::MissingTranslationData.new(locale, key, options)) if entry.nil? + + entry = pluralize(locale, entry, count) if count + entry = interpolate(locale, entry, values) if values + end + + entry + end + + # Acts the same as +strftime+, but uses a localized version of the + # format string. Takes a key from the date/time formats translations as + # a format argument (e.g., :short in :'date.formats'). + def localize(locale, object, format = :default, options = {}) + raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime) + + if Symbol === format + key = format + type = object.respond_to?(:sec) ? 'time' : 'date' + format = I18n.t(:"#{type}.formats.#{key}", :locale => locale, :raise => true) + end + + # format = resolve(locale, object, format, options) + format = format.to_s.gsub(/%[aAbBp]/) do |match| + case match + when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday] + when '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday] + when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon] + when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon] + when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format) if object.respond_to? :hour + end + end + + object.strftime(format) + end + + def initialized? + @initialized ||= false + end + + # Returns an array of locales for which translations are available + # ignoring the reserved translation meta data key :i18n. + def available_locales + init_translations unless initialized? + translations.inject([]) do |locales, (locale, data)| + locales << locale unless (data.keys - [:i18n]).empty? + locales + end + end + + def reload! + @initialized = false + @translations = nil + end + + protected + def init_translations + load_translations(*I18n.load_path.flatten) + @initialized = true + end + + def translations + @translations ||= {} + end + + # Looks up a translation from the translations hash. Returns nil if + # eiher key is nil, or locale, scope or key do not exist as a key in the + # nested translations hash. Splits keys or scopes containing dots + # into multiple keys, i.e. currency.format is regarded the same as + # %w(currency format). + def lookup(locale, key, scope = [], options = {}) + return unless key + init_translations unless initialized? + keys = I18n.normalize_keys(locale, key, scope, options[:separator]) + keys.inject(translations) do |result, key| + key = key.to_sym + return nil unless result.is_a?(Hash) && result.has_key?(key) + result = result[key] + result = resolve(locale, key, result, options) if result.is_a?(Symbol) + String === result ? result.dup : result + end + end + + # Evaluates defaults. + # If given subject is an Array, it walks the array and returns the + # first translation that can be resolved. Otherwise it tries to resolve + # the translation directly. + def default(locale, object, subject, options = {}) + options = options.dup.reject { |key, value| key == :default } + case subject + when Array + subject.each do |item| + result = resolve(locale, object, item, options) and return result + end and nil + else + resolve(locale, object, subject, options) + end + end + + # Resolves a translation. + # If the given subject is a Symbol, it will be translated with the + # given options. If it is a Proc then it will be evaluated. All other + # subjects will be returned directly. + def resolve(locale, object, subject, options = nil) + return subject if options[:resolve] == false + case subject + when Symbol + I18n.translate(subject, (options || {}).merge(:locale => locale, :raise => true)) + when Proc + resolve(locale, object, subject.call(object, options), options = {}) + else + subject + end + rescue MissingTranslationData + nil + end + + # Picks a translation from an array according to English pluralization + # rules. It will pick the first translation if count is not equal to 1 + # and the second translation if it is equal to 1. Other backends can + # implement more flexible or complex pluralization rules. + def pluralize(locale, entry, count) + return entry unless entry.is_a?(Hash) and count + + key = :zero if count == 0 && entry.has_key?(:zero) + key ||= count == 1 ? :one : :other + raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key) + entry[key] + end + + # Interpolates values into a given string. + # + # interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X' + # # => "file test.txt opened by {{user}}" + # + # Note that you have to double escape the \\ when you want to escape + # the {{...}} key in a string (once for the string and once for the + # interpolation). + def interpolate(locale, string, values = {}) + return string unless string.is_a?(::String) && !values.empty? + + preserve_encoding(string) do + s = string.gsub(INTERPOLATION_SYNTAX_PATTERN) do + escaped, key = $1, $2.to_sym + if escaped + "{{#{key}}}" + elsif RESERVED_KEYS.include?(key) + raise ReservedInterpolationKey.new(key, string) + else + "%{#{key}}" + end + end + + values.each do |key, value| + value = value.call(values) if interpolate_lambda?(value, s, key) + value = value.to_s unless value.is_a?(::String) + values[key] = value + end + + s % values + end + + rescue KeyError => e + raise MissingInterpolationArgument.new(values, string) + end + + def preserve_encoding(string) + if string.respond_to?(:encoding) + encoding = string.encoding + result = yield + result.force_encoding(encoding) if result.respond_to?(:force_encoding) + result + else + yield + end + end + + # returns true when the given value responds to :call and the key is + # an interpolation placeholder in the given string + def interpolate_lambda?(object, string, key) + object.respond_to?(:call) && string =~ /%\{#{key}\}|%\<#{key}>.*?\d*\.?\d*[bBdiouxXeEfgGcps]\}/ + end + + # Loads a single translations file by delegating to #load_rb or + # #load_yml depending on the file extension and directly merges the + # data to the existing translations. Raises I18n::UnknownFileType + # for all other file extensions. + def load_file(filename) + type = File.extname(filename).tr('.', '').downcase + raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}") + data = send(:"load_#{type}", filename) # TODO raise a meaningful exception if this does not yield a Hash + data.each { |locale, d| merge_translations(locale, d) } + end + + # Loads a plain Ruby translations file. eval'ing the file must yield + # a Hash containing translation data with locales as toplevel keys. + def load_rb(filename) + eval(IO.read(filename), binding, filename) + end + + # Loads a YAML translations file. The data must have locales as + # toplevel keys. + def load_yml(filename) + YAML::load(IO.read(filename)) + end + + # Deep merges the given translations hash with the existing translations + # for the given locale + def merge_translations(locale, data, options = {}) + locale = locale.to_sym + translations[locale] ||= {} + separator = options[:separator] || I18n.default_separator + data = unwind_keys(data, separator) + data = deep_symbolize_keys(data) + + # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809 + merger = proc do |key, v1, v2| + # TODO should probably be: + # raise TypeError.new("can't merge #{v1.inspect} and #{v2.inspect}") unless Hash === v1 && Hash === v2 + Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : (v2 || v1) + end + translations[locale].merge!(data, &merger) + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cache.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cache.rb new file mode 100644 index 0000000..86a0b27 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cache.rb @@ -0,0 +1,76 @@ +# encoding: utf-8 + +# This module allows you to easily cache all responses from the backend - thus +# speeding up the I18n aspects of your application quite a bit. +# +# To enable caching you can simply include the Cache module to the Simple +# backend - or whatever other backend you are using: +# +# I18n::Backend::Simple.send(:include, I18n::Backend::Cache) +# +# You will also need to set a cache store implementation that you want to use: +# +# I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store) +# +# You can use any cache implementation you want that provides the same API as +# ActiveSupport::Cache (only the methods #fetch and #write are being used). +# +# The cache_key implementation assumes that you only pass values to +# I18n.translate that return a valid key from #hash (see +# http://www.ruby-doc.org/core/classes/Object.html#M000337). +module I18n + class << self + @@cache_store = nil + @@cache_namespace = nil + + def cache_store + @@cache_store + end + + def cache_store=(store) + @@cache_store = store + end + + def cache_namespace + @@cache_namespace + end + + def cache_namespace=(namespace) + @@cache_namespace = namespace + end + + def perform_caching? + !cache_store.nil? + end + end + + module Backend + module Cache + def translate(*args) + I18n.perform_caching? ? fetch(*args) { super } : super + end + + protected + + def fetch(*args, &block) + result = I18n.cache_store.fetch(cache_key(*args), &block) + raise result if result.is_a?(Exception) + result = result.dup if result.frozen? rescue result + result + rescue MissingTranslationData => exception + I18n.cache_store.write(cache_key(*args), exception) + raise exception + end + + def cache_key(*args) + # This assumes that only simple, native Ruby values are passed to I18n.translate. + # Also, in Ruby < 1.8.7 {}.hash != {}.hash + # (see http://paulbarry.com/articles/2009/09/14/why-rails-3-will-require-ruby-1-8-7) + # If args.inspect does not work for you for some reason, patches are very welcome :) + hash = RUBY_VERSION >= "1.8.7" ? args.hash : args.inspect + keys = ['i18n', I18n.cache_namespace, hash] + keys.compact.join('-') + end + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cascade.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cascade.rb new file mode 100644 index 0000000..9f770aa --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cascade.rb @@ -0,0 +1,58 @@ +# encoding: utf-8 + +# EXPERIMENTAL +# +# The Cascade module adds the ability to do cascading lookups to backends that +# are compatible to the Simple backend. +# +# By cascading lookups we mean that for any key that can not be found the +# Cascade module strips one segment off the scope part of the key and then +# tries to look up the key in that scope. +# +# E.g. when a lookup for the key :"foo.bar.baz" does not yield a result then +# the segment :bar will be stripped off the scope part :"foo.bar" and the new +# scope :foo will be used to look up the key :baz. If that does not succeed +# then the remaining scope segment :foo will be omitted, too, and again the +# key :baz will be looked up (now with no scope). +# +# To enable a cascading lookup one passes the :cascade option: +# +# I18n.t(:'foo.bar.baz', :cascade => true) +# +# This will return the first translation found for :"foo.bar.baz", :"foo.baz" +# or :baz in this order. +# +# The cascading lookup takes precedence over resolving any given defaults. +# I.e. defaults will kick in after the cascading lookups haven't succeeded. +# +# This behavior is useful for libraries like ActiveRecord validations where +# the library wants to give users a bunch of more or less fine-grained options +# of scopes for a particular key. +# +# Thanks to Clemens Kofler for the initial idea and implementation! See +# http://github.com/clemens/i18n-cascading-backend + +module I18n + module Backend + module Cascade + def lookup(locale, key, scope = [], options = {}) + return unless key + return super unless cascade = options[:cascade] + + separator = options[:separator] || I18n.default_separator + skip_root = cascade.has_key?(:skip_root) ? cascade[:skip_root] : true + step = cascade[:step] + + keys = I18n.normalize_keys(nil, key, nil, separator) + offset = options[:cascade][:offset] || keys.length + scope = I18n.normalize_keys(nil, nil, scope, separator) + keys + key = scope.slice!(-offset, offset).join(separator) + + begin + result = super + return result unless result.nil? + end while !scope.empty? && scope.slice!(-step, step) && (!scope.empty? || !skip_root) + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/chain.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/chain.rb new file mode 100644 index 0000000..7ab1f99 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/chain.rb @@ -0,0 +1,75 @@ +# encoding: utf-8 + +module I18n + module Backend + # Backend that chains multiple other backends and checks each of them when + # a translation needs to be looked up. This is useful when you want to use + # standard translations with a Simple backend but store custom application + # translations in a database or other backends. + # + # To use the Chain backend instantiate it and set it to the I18n module. + # You can add chained backends through the initializer or backends + # accessor: + # + # # preserves the existing Simple backend set to I18n.backend + # I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend) + # + # The implementation assumes that all backends added to the Chain implement + # a lookup method with the same API as Simple backend does. + class Chain < Simple + attr_accessor :backends + + def initialize(*backends) + self.backends = backends + end + + def reload! + backends.each { |backend| backend.reload! } + end + + def store_translations(locale, data, options = {}) + backends.first.store_translations(locale, data, options = {}) + end + + def available_locales + backends.map { |backend| backend.available_locales }.flatten.uniq + end + + def translate(locale, key, options = {}) + return key.map { |k| translate(locale, k, options) } if key.is_a?(Array) + + default = options.delete(:default) + namespace = {} + backends.each do |backend| + begin + options.update(:default => default) if default and backend == backends.last + translation = backend.translate(locale, key, options) + if namespace_lookup?(translation, options) + namespace.update(translation) + elsif !translation.nil? + return translation + end + rescue MissingTranslationData + end + end + return namespace unless namespace.empty? + raise(I18n::MissingTranslationData.new(locale, key, options)) + end + + def localize(locale, object, format = :default, options = {}) + backends.each do |backend| + begin + result = backend.localize(locale, object, format, options) and return result + rescue MissingTranslationData + end + end + raise(I18n::MissingTranslationData.new(locale, format, options)) + end + + protected + def namespace_lookup?(result, options) + result.is_a?(Hash) and not options.has_key?(:count) + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cldr.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cldr.rb new file mode 100644 index 0000000..d702f22 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/cldr.rb @@ -0,0 +1,100 @@ +# encoding: utf-8 +require 'cldr' + +module I18n + module Backend + module Cldr + include ::Cldr::Format + + def localize(locale, object, format = :default, options = {}) + options[:as] ||= detect_type(object, options) + send(:"format_#{options[:as]}", locale, object, format, options) + end + + def format_decimal(locale, object, format = :default, options = {}) + formatter(locale, :decimal, format).apply(object, options) + end + + def format_integer(locale, object, format = :default, options = {}) + format_object(number, options.merge(:precision => 0)) + end + + def format_currency(locale, object, format = :default, options = {}) + options.merge!(:currency => lookup_currency(locale, options[:currency], object)) if options[:currency].is_a?(Symbol) + formatter(locale, :currency, format).apply(object, options) + end + + def format_percent(locale, object, format = :default, options = {}) + formatter(locale, :percent, format).apply(object, options) + end + + def format_date(locale, object, format = :default, options = {}) + formatter(locale, :date, format).apply(object, options) + end + + def format_time(locale, object, format = :default, options = {}) + formatter(locale, :time, format).apply(object, options) + end + + def format_datetime(locale, object, format = :default, options = {}) + key = :"calendars.gregorian.formats.datetime.#{format}.pattern" + date = I18n.l(object, :format => options[:date_format] || format, :locale => locale, :as => :date) + time = I18n.l(object, :format => options[:time_format] || format, :locale => locale, :as => :time) + I18n.t(key, :date => date, :time => time, :locale => locale, :raise => true) + end + + protected + + def detect_type(object, options) + options.has_key?(:currency) ? :currency : case object + when ::Numeric + :decimal + when ::Date, ::DateTime, ::Time + object.class.name.downcase.to_sym + else + raise_unspecified_format_type! + end + end + + def formatter(locale, type, format) + (@formatters ||= {})[:"#{locale}.#{type}.#{format}"] ||= begin + format = lookup_format(locale, type, format) + data = lookup_format_data(locale, type) + ::Cldr::Format.const_get(type.to_s.camelize).new(format, data) + end + end + + def lookup_format(locale, type, format) + key = case type + when :date, :time, :datetime + :"calendars.gregorian.formats.#{type}.#{format}.pattern" + else + :"numbers.formats.#{type}.patterns.#{format || :default}" + end + I18n.t(key, :locale => locale, :raise => true) + end + + def lookup_format_data(locale, type) + key = case type + when :date, :time, :datetime + :'calendars.gregorian' + else + :'numbers.symbols' + end + I18n.t(key, :locale => locale, :raise => true) + end + + def lookup_currency(locale, currency, count) + I18n.t(:"currencies.#{currency}", :locale => locale, :count => count) + end + + def raise_unspecified_format_type! + raise ArgumentError.new("You have to specify a format type, e.g. :as => :number.") + end + + def raise_unspecified_currency! + raise ArgumentError.new("You have to specify a currency, e.g. :currency => 'EUR'.") + end + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/fallbacks.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/fallbacks.rb new file mode 100644 index 0000000..1e014be --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/fallbacks.rb @@ -0,0 +1,69 @@ +# encoding: utf-8 + +# I18n locale fallbacks are useful when you want your application to use +# translations from other locales when translations for the current locale are +# missing. E.g. you might want to use :en translations when translations in +# your applications main locale :de are missing. +# +# To enable locale fallbacks you can simply include the Fallbacks module to +# the Simple backend - or whatever other backend you are using: +# +# I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) +module I18n + @@fallbacks = nil + + class << self + # Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+. + def fallbacks + @@fallbacks ||= I18n::Locale::Fallbacks.new + end + + # Sets the current fallbacks implementation. Use this to set a different fallbacks implementation. + def fallbacks=(fallbacks) + @@fallbacks = fallbacks + end + end + + module Backend + module Fallbacks + # Overwrites the Base backend translate method so that it will try each + # locale given by I18n.fallbacks for the given locale. E.g. for the + # locale :"de-DE" it might try the locales :"de-DE", :de and :en + # (depends on the fallbacks implementation) until it finds a result with + # the given options. If it does not find any result for any of the + # locales it will then raise a MissingTranslationData exception as + # usual. + # + # The default option takes precedence over fallback locales + # only when it's not a String. When default contains String it + # is evaluated after fallback locales. + def translate(locale, key, options = {}) + default = extract_string_default!(options) if options[:default] + + I18n.fallbacks[locale].each do |fallback| + begin + result = super(fallback, key, options) + return result unless result.nil? + rescue I18n::MissingTranslationData + end + end + + return super(locale, nil, options.merge(:default => default)) if default + raise(I18n::MissingTranslationData.new(locale, key, options)) + end + + def extract_string_default!(options) + defaults = Array(options[:default]) + if index = find_first_string_default(defaults) + options[:default] = defaults[0, index] + defaults[index] + end + end + + def find_first_string_default(defaults) + defaults.each_index { |ix| return ix if String === defaults[ix] } + nil + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/fast.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/fast.rb new file mode 100644 index 0000000..9656e59 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/fast.rb @@ -0,0 +1,69 @@ +# encoding: utf-8 + +# The Fast module contains optimizations that can tremendously speed up the +# lookup process on the Simple backend. It works by flattening the nested +# translation hash to a flat hash (e.g. { :a => { :b => 'c' } } becomes +# { :'a.b' => 'c' }). +# +# To enable these optimizations you can simply include the Fast module to +# the Simple backend: +# +# I18n::Backend::Simple.send(:include, I18n::Backend::Fast) +module I18n + module Backend + module Fast + include Links + + def reset_flattened_translations! + @flattened_translations = nil + end + + def flattened_translations + @flattened_translations ||= flatten_translations(translations) + end + + def merge_translations(locale, data, options = {}) + super + reset_flattened_translations! + end + + def init_translations + super + reset_flattened_translations! + end + + protected + def flatten_translations(translations) + # don't flatten locale roots + translations.inject({}) do |result, (locale, translations)| + result[locale] = wind_keys(translations, nil, true) + result[locale].each do |key, value| + store_link(locale, key, value) if value.is_a?(Symbol) + end + result + end + end + + def lookup(locale, key, scope = nil, options = {}) + return unless key + init_translations unless initialized? + + return nil unless flattened_translations.has_key?(locale.to_sym) + + separator = options[:separator] + if separator && I18n.default_separator != separator + key = cleanup_non_standard_separator(key, separator) + scope = Array(scope).map{|k| cleanup_non_standard_separator(k, separator)} if scope + end + + key = resolve_link(locale, key) + key = (Array(scope) + [key]).join(I18n.default_separator) if scope + flattened_translations[locale.to_sym][key.to_sym] + end + + def cleanup_non_standard_separator(key, user_separator) + escape_default_separator(key).tr(user_separator, I18n.default_separator) + end + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/gettext.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/gettext.rb new file mode 100644 index 0000000..7f8ad22 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/gettext.rb @@ -0,0 +1,75 @@ +# encoding: utf-8 + +require 'i18n/gettext' +require 'i18n/gettext/po_parser' + +# Experimental support for using Gettext po files to store translations. +# +# To use this you can simply include the module to the Simple backend - or +# whatever other backend you are using. +# +# I18n::Backend::Simple.send(:include, I18n::Backend::Gettext) +# +# Now you should be able to include your Gettext translation (*.po) files to +# the I18n.load_path so they're loaded to the backend and you can use them as +# usual: +# +# I18n.load_path += Dir["path/to/locales/*.po"] +# +# Following the Gettext convention this implementation expects that your +# translation files are named by their locales. E.g. the file en.po would +# contain the translations for the English locale. +module I18n + module Backend + module Gettext + class PoData < Hash + def set_comment(msgid_or_sym, comment) + # ignore + end + end + + protected + def load_po(filename) + locale = ::File.basename(filename, '.po').to_sym + data = normalize(locale, parse(filename)) + { locale => data } + end + + def parse(filename) + GetText::PoParser.new.parse(::File.read(filename), PoData.new) + end + + def normalize(locale, data) + data.inject({}) do |result, (key, value)| + unless key.nil? || key.empty? + key, value = normalize_pluralization(locale, key, value) if key.index("\000") + + parts = key.split('|').reverse + normalized = parts.inject({}) do |normalized, part| + normalized = { part => normalized.empty? ? value : normalized } + end + + # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809 + merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 } + result.merge!(normalized, &merger) + end + result + end + end + + def normalize_pluralization(locale, key, value) + # FIXME po_parser includes \000 chars that can not be turned into Symbols + key = key.gsub("\000", I18n::Gettext::PLURAL_SEPARATOR).split(I18n::Gettext::PLURAL_SEPARATOR).first + + keys = I18n::Gettext.plural_keys(locale) + values = value.split("\000") + raise "invalid number of plurals: #{values.size}, keys: #{keys.inspect}" if values.size != keys.size + + result = {} + values.each_with_index { |value, ix| result[keys[ix]] = value } + [key, result] + end + + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/helpers.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/helpers.rb new file mode 100644 index 0000000..db773f2 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/helpers.rb @@ -0,0 +1,68 @@ +module I18n + module Backend + module Helpers + SEPARATOR_ESCAPE_CHAR = "\001" + + # Return a new hash with all keys and nested keys converted to symbols. + def deep_symbolize_keys(hash) + hash.inject({}) { |result, (key, value)| + value = deep_symbolize_keys(value) if value.is_a?(Hash) + result[(key.to_sym rescue key) || key] = value + result + } + end + + # Flatten keys for nested Hashes by chaining up keys using the separator + # >> { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}.wind + # => { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" } + def wind_keys(hash, separator = nil, subtree = false, prev_key = nil, result = {}, orig_hash=hash) + separator ||= I18n.default_separator + + hash.each_pair do |key, value| + key = escape_default_separator(key, separator) + curr_key = [prev_key, key].compact.join(separator).to_sym + + if value.is_a?(Hash) + result[curr_key] = value if subtree + wind_keys(value, separator, subtree, curr_key, result, orig_hash) + else + result[unescape_default_separator(curr_key)] = value + end + end + + result + end + + def escape_default_separator(key, separator=nil) + key.to_s.tr(separator || I18n.default_separator, SEPARATOR_ESCAPE_CHAR) + end + + def unescape_default_separator(key, separator=nil) + key.to_s.tr(SEPARATOR_ESCAPE_CHAR, separator || I18n.default_separator).to_sym + end + + # Expand keys chained by the the given separator through nested Hashes + # >> { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" }.unwind + # => { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"} + def unwind_keys(hash, separator = ".") + result = {} + hash.each do |key, value| + keys = key.to_s.split(separator) + curr = result + curr = curr[keys.shift] ||= {} while keys.size > 1 + curr[keys.shift] = value + end + result + end + + # # Flatten the given array once + # def flatten_once(array) + # result = [] + # for element in array # a little faster than each + # result.push(*element) + # end + # result + # end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/interpolation_compiler.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/interpolation_compiler.rb new file mode 100644 index 0000000..702afdb --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/interpolation_compiler.rb @@ -0,0 +1,119 @@ +# encoding: utf-8 + +# The InterpolationCompiler module contains optimizations that can tremendously +# speed up the interpolation process on the Simple backend. +# +# It works by defining a pre-compiled method on stored translation Strings that +# already bring all the knowledge about contained interpolation variables etc. +# so that the actual recurring interpolation will be very fast. +# +# To enable pre-compiled interpolations you can simply include the +# InterpolationCompiler module to the Simple backend: +# +# I18n::Backend::Simple.send(:include, I18n::Backend::InterpolationCompiler) +module I18n + module Backend + module InterpolationCompiler + module Compiler + extend self + + TOKENIZER = /(\\\{\{[^\}]+\}\}|\{\{[^\}]+\}\})/ + INTERPOLATION_SYNTAX_PATTERN = /(\\)?(\{\{([^\}]+)\}\})/ + + def compile_if_an_interpolation(string) + if interpolated_str?(string) + string.instance_eval <<-RUBY_EVAL, __FILE__, __LINE__ + def i18n_interpolate(v = {}) + "#{compiled_interpolation_body(string)}" + end + RUBY_EVAL + end + + string + end + + def interpolated_str?(str) + str.kind_of?(::String) && str =~ INTERPOLATION_SYNTAX_PATTERN + end + + protected + # tokenize("foo {{bar}} baz \\{{buz}}") # => ["foo ", "{{bar}}", " baz ", "\\{{buz}}"] + def tokenize(str) + str.split(TOKENIZER) + end + + def compiled_interpolation_body(str) + tokenize(str).map do |token| + (matchdata = token.match(INTERPOLATION_SYNTAX_PATTERN)) ? handle_interpolation_token(token, matchdata) : escape_plain_str(token) + end.join + end + + def handle_interpolation_token(interpolation, matchdata) + escaped, pattern, key = matchdata.values_at(1, 2, 3) + escaped ? pattern : compile_interpolation_token(key.to_sym) + end + + def compile_interpolation_token(key) + "\#{#{interpolate_or_raise_missing(key)}}" + end + + def interpolate_or_raise_missing(key) + escaped_key = escape_key_sym(key) + Base::RESERVED_KEYS.include?(key) ? reserved_key(escaped_key) : interpolate_key(escaped_key) + end + + def interpolate_key(key) + [direct_key(key), nil_key(key), missing_key(key)].join('||') + end + + def direct_key(key) + "((t = v[#{key}]) && t.respond_to?(:call) ? t.call : t)" + end + + def nil_key(key) + "(v.has_key?(#{key}) && '')" + end + + def missing_key(key) + "raise(MissingInterpolationArgument.new(#{key}, self))" + end + + def reserved_key(key) + "raise(ReservedInterpolationKey.new(#{key}, self))" + end + + def escape_plain_str(str) + str.gsub(/"|\\|#/) {|x| "\\#{x}"} + end + + def escape_key_sym(key) + # rely on Ruby to do all the hard work :) + key.to_sym.inspect + end + end + + def interpolate(locale, string, values) + if string.respond_to?(:i18n_interpolate) + string.i18n_interpolate(values) + elsif values + super + else + string + end + end + + def merge_translations(locale, data, options = {}) + compile_all_strings_in(data) + super + end + + protected + def compile_all_strings_in(data) + data.each_value do |value| + Compiler.compile_if_an_interpolation(value) + compile_all_strings_in(value) if value.kind_of?(Hash) + end + end + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/links.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/links.rb new file mode 100644 index 0000000..5f99bc8 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/links.rb @@ -0,0 +1,34 @@ +module I18n + module Backend + module Links + protected + def links(locale) + @links ||= {} + @links[locale.to_sym] ||= {} + end + + def store_link(locale, key, link) + links(locale)[key.to_s] = link.to_s + end + + def resolve_link(locale, key) + key = key.to_s + links = self.links(locale) + + if links.key?(key) + links[key] + elsif link = find_link(locale, key) + store_link(locale, key, key.gsub(*link)) + else + key + end + end + + def find_link(locale, key) + links(locale).each do |from, to| + return [from, to] if key[0, from.length] == from + end && nil + end + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/metadata.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/metadata.rb new file mode 100644 index 0000000..3ebb8af --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/metadata.rb @@ -0,0 +1,73 @@ +# I18n translation metadata is useful when you want to access information +# about how a translation was looked up, pluralized or interpolated in +# your application. +# +# msg = I18n.t(:message, :default => 'Hi!', :scope => :foo) +# msg.translation_metadata +# # => { :key => :message, :scope => :foo, :default => 'Hi!' } +# +# If a :count option was passed to #translate it will be set to the metadata. +# Likewise, if any interpolation variables were passed they will also be set. +# +# To enable translation metadata you can simply include the Metadata module +# into the Simple backend class - or whatever other backend you are using: +# +# I18n::Backend::Simple.send(:include, I18n::Backend::Metadata) + +require 'i18n/core_ext/object/meta_class' + +module I18n + module Backend + module Metadata + class << self + def included(base) + Object.class_eval do + def translation_metadata + @translation_metadata ||= {} + end + + def translation_metadata=(translation_metadata) + @translation_metadata = translation_metadata + end + end + end + end + + def translate(locale, key, options = {}) + metadata = { + :locale => locale, + :key => key, + :scope => options[:scope], + :default => options[:default], + :separator => options[:separator], + :values => options.reject { |name, value| Base::RESERVED_KEYS.include?(name) } + } + with_metadata(metadata) { super } + end + + def interpolate(locale, string, values = {}) + with_metadata(:original => string) do + preserve_translation_metadata(string) { super } + end if string + end + + def pluralize(locale, entry, count) + with_metadata(:count => count) { super } + end + + protected + + def with_metadata(metadata, &block) + result = yield + result.translation_metadata = result.translation_metadata.merge(metadata) if result + result + end + + def preserve_translation_metadata(object, &block) + result = yield + result.translation_metadata = object.translation_metadata if result + result + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/pluralization.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/pluralization.rb new file mode 100644 index 0000000..33a2aff --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/pluralization.rb @@ -0,0 +1,57 @@ +# encoding: utf-8 + +# I18n locale fallbacks are useful when you want your application to use +# translations from other locales when translations for the current locale are +# missing. E.g. you might want to use :en translations when translations in +# your applications main locale :de are missing. +# +# To enable locale specific pluralizations you can simply include the +# Pluralization module to the Simple backend - or whatever other backend you +# are using. +# +# I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization) +# +# You also need to make sure to provide pluralization algorithms to the +# backend, i.e. include them to your I18n.load_path accordingly. +module I18n + module Backend + module Pluralization + # Overwrites the Base backend translate method so that it will check the + # translation meta data space (:i18n) for a locale specific pluralization + # rule and use it to pluralize the given entry. I.e. the library expects + # pluralization rules to be stored at I18n.t(:'i18n.plural.rule') + # + # Pluralization rules are expected to respond to #call(entry, count) and + # return a pluralization key. Valid keys depend on the translation data + # hash (entry) but it is generally recommended to follow CLDR's style, + # i.e., return one of the keys :zero, :one, :few, :many, :other. + # + # The :zero key is always picked directly when count equals 0 AND the + # translation data has the key :zero. This way translators are free to + # either pick a special :zero translation even for languages where the + # pluralizer does not return a :zero key. + def pluralize(locale, entry, count) + return entry unless entry.is_a?(Hash) and count + + pluralizer = pluralizer(locale) + if pluralizer.respond_to?(:call) + key = count == 0 && entry.has_key?(:zero) ? :zero : pluralizer.call(count) + raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key) + entry[key] + else + super + end + end + + protected + + def pluralizers + @pluralizers ||= {} + end + + def pluralizer(locale) + pluralizers[locale] ||= I18n.t(:'i18n.plural.rule', :locale => locale, :resolve => false) + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/simple.rb new file mode 100644 index 0000000..1454a10 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/backend/simple.rb @@ -0,0 +1,22 @@ +# encoding: utf-8 + +# Stub class for the Simple backend. The actual implementation is provided by +# the backend Base class. This makes it easier to extend the Simple backend's +# behaviour by including modules. E.g.: +# +# module I18n::Backend::Pluralization +# def pluralize(*args) +# # extended pluralization logic +# super +# end +# end +# +# I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization) + +module I18n + module Backend + class Simple + include Base + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/hash/except.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/hash/except.rb new file mode 100644 index 0000000..a228148 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/hash/except.rb @@ -0,0 +1,8 @@ +# from facets (http://facets.rubyforge.org) +require 'i18n/core_ext/hash/slice' + +class Hash + def except(*less_keys) + slice(*keys - less_keys) + end +end unless Hash.method_defined?(:except) \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/hash/slice.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/hash/slice.rb new file mode 100644 index 0000000..90e5168 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/hash/slice.rb @@ -0,0 +1,8 @@ +# from facets (http://facets.rubyforge.org) +class Hash + def slice(*keep_keys) + h = {} + keep_keys.each { |key| h[key] = fetch(key) } + h + end +end unless Hash.new.respond_to?(:slice) \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/object/meta_class.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/object/meta_class.rb new file mode 100644 index 0000000..e6d51f7 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/object/meta_class.rb @@ -0,0 +1,5 @@ +Object.class_eval do + def meta_class + class << self; self; end + end +end unless Object.method_defined?(:meta_class) diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/string/interpolate.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/string/interpolate.rb new file mode 100644 index 0000000..94b1a36 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/core_ext/string/interpolate.rb @@ -0,0 +1,99 @@ +# encoding: utf-8 + +=begin + heavily based on Masao Mutoh's gettext String interpolation extension + http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb + Copyright (C) 2005-2009 Masao Mutoh + You may redistribute it and/or modify it under the same license terms as Ruby. +=end + +if RUBY_VERSION < '1.9' + + # KeyError is raised by String#% when the string contains a named placeholder + # that is not contained in the given arguments hash. Ruby 1.9 includes and + # raises this exception natively. We define it to mimic Ruby 1.9's behaviour + # in Ruby 1.8.x + + class KeyError < IndexError + def initialize(message = nil) + super(message || "key not found") + end + end unless defined?(KeyError) + + # Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError. + # + # String#% method which accept "named argument". The translator can know + # the meaning of the msgids using "named argument" instead of %s/%d style. + + class String + # For older ruby versions, such as ruby-1.8.5 + alias :bytesize :size unless instance_methods.find {|m| m.to_s == 'bytesize'} + alias :interpolate_without_ruby_19_syntax :% # :nodoc: + + INTERPOLATION_PATTERN = Regexp.union( + /%\{(\w+)\}/, # matches placeholders like "%{foo}" + /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%.d" + ) + + INTERPOLATION_PATTERN_WITH_ESCAPE = Regexp.union( + /%%/, + INTERPOLATION_PATTERN + ) + + # % uses self (i.e. the String) as a format specification and returns the + # result of applying it to the given arguments. In other words it interpolates + # the given arguments to the string according to the formats the string + # defines. + # + # There are three ways to use it: + # + # * Using a single argument or Array of arguments. + # + # This is the default behaviour of the String class. See Kernel#sprintf for + # more details about the format string. + # + # Example: + # + # "%d %s" % [1, "message"] + # # => "1 message" + # + # * Using a Hash as an argument and unformatted, named placeholders. + # + # When you pass a Hash as an argument and specify placeholders with %{foo} + # it will interpret the hash values as named arguments. + # + # Example: + # + # "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"} + # # => "Masao Mutoh" + # + # * Using a Hash as an argument and formatted, named placeholders. + # + # When you pass a Hash as an argument and specify placeholders with %d + # it will interpret the hash values as named arguments and format the value + # according to the formatting instruction appended to the closing >. + # + # Example: + # + # "%d, %.1f" % { :integer => 10, :float => 43.4 } + # # => "10, 43.3" + def %(args) + if args.kind_of?(Hash) + dup.gsub(INTERPOLATION_PATTERN_WITH_ESCAPE) do |match| + if match == '%%' + '%' + else + key = ($1 || $2).to_sym + raise KeyError unless args.has_key?(key) + $3 ? sprintf("%#{$3}", args[key]) : args[key] + end + end + elsif self =~ INTERPOLATION_PATTERN + raise ArgumentError.new('one hash required') + else + result = gsub(/%([{<])/, '%%\1') + result.send :'interpolate_without_ruby_19_syntax', args + end + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/exceptions.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/exceptions.rb new file mode 100644 index 0000000..766a171 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/exceptions.rb @@ -0,0 +1,61 @@ +# encoding: utf-8 + +class KeyError < IndexError + def initialize(message = nil) + super(message || "key not found") + end +end unless defined?(KeyError) + +module I18n + class ArgumentError < ::ArgumentError; end + + class InvalidLocale < ArgumentError + attr_reader :locale + def initialize(locale) + @locale = locale + super "#{locale.inspect} is not a valid locale" + end + end + + class MissingTranslationData < ArgumentError + attr_reader :locale, :key, :options + def initialize(locale, key, opts = nil) + @key, @locale, @options = key, locale, opts || {} + keys = I18n.normalize_keys(locale, key, options[:scope]) + keys << 'no key' if keys.size < 2 + super "translation missing: #{keys.join(', ')}" + end + end + + class InvalidPluralizationData < ArgumentError + attr_reader :entry, :count + def initialize(entry, count) + @entry, @count = entry, count + super "translation data #{entry.inspect} can not be used with :count => #{count}" + end + end + + class MissingInterpolationArgument < ArgumentError + attr_reader :values, :string + def initialize(values, string) + @values, @string = values, string + super "missing interpolation argument in #{string.inspect} (#{values.inspect} given)" + end + end + + class ReservedInterpolationKey < ArgumentError + attr_reader :key, :string + def initialize(key, string) + @key, @string = key, string + super "reserved key #{key.inspect} used in #{string.inspect}" + end + end + + class UnknownFileType < ArgumentError + attr_reader :type, :filename + def initialize(type, filename) + @type, @filename = type, filename + super "can not load translations from #{filename}, the file type #{type} is not known" + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/gettext.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/gettext.rb new file mode 100644 index 0000000..b8dec6e --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/gettext.rb @@ -0,0 +1,25 @@ +# encoding: utf-8 + +module I18n + module Gettext + PLURAL_SEPARATOR = "\001" + CONTEXT_SEPARATOR = "\004" + + @@plural_keys = { :en => [:one, :other] } + + class << self + # returns an array of plural keys for the given locale so that we can + # convert from gettext's integer-index based style + # TODO move this information to the pluralization module + def plural_keys(locale) + @@plural_keys[locale] || @@plural_keys[:en] + end + + def extract_scope(msgid, separator) + scope = msgid.to_s.split(separator) + msgid = scope.pop + [scope, msgid] + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/gettext/po_parser.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/gettext/po_parser.rb new file mode 100644 index 0000000..547df6a --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/gettext/po_parser.rb @@ -0,0 +1,329 @@ +=begin + poparser.rb - Generate a .mo + + Copyright (C) 2003-2009 Masao Mutoh + + You may redistribute it and/or modify it under the same + license terms as Ruby. +=end + +#MODIFIED +# removed include GetText etc +# added stub translation method _(x) +require 'racc/parser' + +module GetText + + class PoParser < Racc::Parser + + def _(x) + x + end + +module_eval <<'..end src/poparser.ry modeval..id7a99570e05', 'src/poparser.ry', 108 + def unescape(orig) + ret = orig.gsub(/\\n/, "\n") + ret.gsub!(/\\t/, "\t") + ret.gsub!(/\\r/, "\r") + ret.gsub!(/\\"/, "\"") + ret + end + + def parse(str, data, ignore_fuzzy = true) + @comments = [] + @data = data + @fuzzy = false + @msgctxt = "" + $ignore_fuzzy = ignore_fuzzy + + str.strip! + @q = [] + until str.empty? do + case str + when /\A\s+/ + str = $' + when /\Amsgctxt/ + @q.push [:MSGCTXT, $&] + str = $' + when /\Amsgid_plural/ + @q.push [:MSGID_PLURAL, $&] + str = $' + when /\Amsgid/ + @q.push [:MSGID, $&] + str = $' + when /\Amsgstr/ + @q.push [:MSGSTR, $&] + str = $' + when /\A\[(\d+)\]/ + @q.push [:PLURAL_NUM, $1] + str = $' + when /\A\#~(.*)/ + $stderr.print _("Warning: obsolete msgid exists.\n") + $stderr.print " #{$&}\n" + @q.push [:COMMENT, $&] + str = $' + when /\A\#(.*)/ + @q.push [:COMMENT, $&] + str = $' + when /\A\"(.*)\"/ + @q.push [:STRING, $1] + str = $' + else + #c = str[0,1] + #@q.push [:STRING, c] + str = str[1..-1] + end + end + @q.push [false, '$end'] + if $DEBUG + @q.each do |a,b| + puts "[#{a}, #{b}]" + end + end + @yydebug = true if $DEBUG + do_parse + + if @comments.size > 0 + @data.set_comment(:last, @comments.join("\n")) + end + @data + end + + def next_token + @q.shift + end + + def on_message(msgid, msgstr) + if msgstr.size > 0 + @data[msgid] = msgstr + @data.set_comment(msgid, @comments.join("\n")) + end + @comments.clear + @msgctxt = "" + end + + def on_comment(comment) + @fuzzy = true if (/fuzzy/ =~ comment) + @comments << comment + end + + +..end src/poparser.ry modeval..id7a99570e05 + +##### racc 1.4.5 generates ### + +racc_reduce_table = [ + 0, 0, :racc_error, + 0, 10, :_reduce_none, + 2, 10, :_reduce_none, + 2, 10, :_reduce_none, + 2, 10, :_reduce_none, + 2, 12, :_reduce_5, + 1, 13, :_reduce_none, + 1, 13, :_reduce_none, + 4, 15, :_reduce_8, + 5, 16, :_reduce_9, + 2, 17, :_reduce_10, + 1, 17, :_reduce_none, + 3, 18, :_reduce_12, + 1, 11, :_reduce_13, + 2, 14, :_reduce_14, + 1, 14, :_reduce_15 ] + +racc_reduce_n = 16 + +racc_shift_n = 26 + +racc_action_table = [ + 3, 13, 5, 7, 9, 15, 16, 17, 20, 17, + 13, 17, 13, 13, 11, 17, 23, 20, 13, 17 ] + +racc_action_check = [ + 1, 16, 1, 1, 1, 12, 12, 12, 18, 18, + 7, 14, 15, 9, 3, 19, 20, 21, 23, 25 ] + +racc_action_pointer = [ + nil, 0, nil, 14, nil, nil, nil, 3, nil, 6, + nil, nil, 0, nil, 4, 5, -6, nil, 2, 8, + 8, 11, nil, 11, nil, 12 ] + +racc_action_default = [ + -1, -16, -2, -16, -3, -13, -4, -16, -6, -16, + -7, 26, -16, -15, -5, -16, -16, -14, -16, -8, + -16, -9, -11, -16, -10, -12 ] + +racc_goto_table = [ + 12, 22, 14, 4, 24, 6, 2, 8, 18, 19, + 10, 21, 1, nil, nil, nil, 25 ] + +racc_goto_check = [ + 5, 9, 5, 3, 9, 4, 2, 6, 5, 5, + 7, 8, 1, nil, nil, nil, 5 ] + +racc_goto_pointer = [ + nil, 12, 5, 2, 4, -7, 6, 9, -7, -17 ] + +racc_goto_default = [ + nil, nil, nil, nil, nil, nil, nil, nil, nil, nil ] + +racc_token_table = { + false => 0, + Object.new => 1, + :COMMENT => 2, + :MSGID => 3, + :MSGCTXT => 4, + :MSGID_PLURAL => 5, + :MSGSTR => 6, + :STRING => 7, + :PLURAL_NUM => 8 } + +racc_use_result_var = true + +racc_nt_base = 9 + +Racc_arg = [ + racc_action_table, + racc_action_check, + racc_action_default, + racc_action_pointer, + racc_goto_table, + racc_goto_check, + racc_goto_default, + racc_goto_pointer, + racc_nt_base, + racc_reduce_table, + racc_token_table, + racc_shift_n, + racc_reduce_n, + racc_use_result_var ] + +Racc_token_to_s_table = [ +'$end', +'error', +'COMMENT', +'MSGID', +'MSGCTXT', +'MSGID_PLURAL', +'MSGSTR', +'STRING', +'PLURAL_NUM', +'$start', +'msgfmt', +'comment', +'msgctxt', +'message', +'string_list', +'single_message', +'plural_message', +'msgstr_plural', +'msgstr_plural_line'] + +Racc_debug_parser = true + +##### racc system variables end ##### + + # reduce 0 omitted + + # reduce 1 omitted + + # reduce 2 omitted + + # reduce 3 omitted + + # reduce 4 omitted + +module_eval <<'.,.,', 'src/poparser.ry', 25 + def _reduce_5( val, _values, result ) + @msgctxt = unescape(val[1]) + "\004" + result + end +.,., + + # reduce 6 omitted + + # reduce 7 omitted + +module_eval <<'.,.,', 'src/poparser.ry', 48 + def _reduce_8( val, _values, result ) + if @fuzzy and $ignore_fuzzy + if val[1] != "" + $stderr.print _("Warning: fuzzy message was ignored.\n") + $stderr.print " msgid '#{val[1]}'\n" + else + on_message('', unescape(val[3])) + end + @fuzzy = false + else + on_message(@msgctxt + unescape(val[1]), unescape(val[3])) + end + result = "" + result + end +.,., + +module_eval <<'.,.,', 'src/poparser.ry', 65 + def _reduce_9( val, _values, result ) + if @fuzzy and $ignore_fuzzy + if val[1] != "" + $stderr.print _("Warning: fuzzy message was ignored.\n") + $stderr.print "msgid = '#{val[1]}\n" + else + on_message('', unescape(val[3])) + end + @fuzzy = false + else + on_message(@msgctxt + unescape(val[1]) + "\000" + unescape(val[3]), unescape(val[4])) + end + result = "" + result + end +.,., + +module_eval <<'.,.,', 'src/poparser.ry', 76 + def _reduce_10( val, _values, result ) + if val[0].size > 0 + result = val[0] + "\000" + val[1] + else + result = "" + end + result + end +.,., + + # reduce 11 omitted + +module_eval <<'.,.,', 'src/poparser.ry', 84 + def _reduce_12( val, _values, result ) + result = val[2] + result + end +.,., + +module_eval <<'.,.,', 'src/poparser.ry', 91 + def _reduce_13( val, _values, result ) + on_comment(val[0]) + result + end +.,., + +module_eval <<'.,.,', 'src/poparser.ry', 99 + def _reduce_14( val, _values, result ) + result = val.delete_if{|item| item == ""}.join + result + end +.,., + +module_eval <<'.,.,', 'src/poparser.ry', 103 + def _reduce_15( val, _values, result ) + result = val[0] + result + end +.,., + + def _reduce_none( val, _values, result ) + result + end + + end # class PoParser + +end # module GetText diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/helpers.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/helpers.rb new file mode 100644 index 0000000..8bbe02e --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/helpers.rb @@ -0,0 +1,5 @@ +module I18n + module Helpers + autoload :Gettext, 'i18n/helpers/gettext' + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/helpers/gettext.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/helpers/gettext.rb new file mode 100644 index 0000000..8285805 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/helpers/gettext.rb @@ -0,0 +1,65 @@ +# encoding: utf-8 +require 'i18n/gettext' + +module I18n + module Helpers + # Implements classical Gettext style accessors. To use this include the + # module to the global namespace or wherever you want to use it. + # + # include I18n::Helpers::Gettext + module Gettext + def gettext(msgid, options = {}) + I18n.t(msgid, { :default => msgid, :separator => '|' }.merge(options)) + end + alias _ gettext + + def sgettext(msgid, separator = '|') + scope, msgid = I18n::Gettext.extract_scope(msgid, separator) + I18n.t(msgid, :scope => scope, :default => msgid, :separator => separator) + end + alias s_ sgettext + + def pgettext(msgctxt, msgid) + separator = I18n::Gettext::CONTEXT_SEPARATOR + sgettext([msgctxt, msgid].join(separator), separator) + end + alias p_ pgettext + + def ngettext(msgid, msgid_plural, n = 1) + nsgettext(msgid, msgid_plural, n) + end + alias n_ ngettext + + # Method signatures: + # nsgettext('Fruits|apple', 'apples', 2) + # nsgettext(['Fruits|apple', 'apples'], 2) + def nsgettext(msgid, msgid_plural, n = 1, separator = '|') + if msgid.is_a?(Array) + msgid, msgid_plural, n, separator = msgid[0], msgid[1], msgid_plural, n + separator = '|' unless separator.is_a?(::String) + end + + scope, msgid = I18n::Gettext.extract_scope(msgid, separator) + default = { :one => msgid, :other => msgid_plural } + I18n.t(msgid, :default => default, :count => n, :scope => scope, :separator => separator) + end + alias ns_ nsgettext + + # Method signatures: + # npgettext('Fruits', 'apple', 'apples', 2) + # npgettext('Fruits', ['apple', 'apples'], 2) + def npgettext(msgctxt, msgid, msgid_plural, n = 1) + separator = I18n::Gettext::CONTEXT_SEPARATOR + + if msgid.is_a?(Array) + msgid_plural, msgid, n = msgid[1], [msgctxt, msgid[0]].join(separator), msgid_plural + else + msgid = [msgctxt, msgid].join(separator) + end + + nsgettext(msgid, msgid_plural, n, separator) + end + alias np_ npgettext + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale.rb new file mode 100644 index 0000000..4f9d026 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale.rb @@ -0,0 +1,6 @@ +module I18n + module Locale + autoload :Fallbacks, 'i18n/locale/fallbacks' + autoload :Tag, 'i18n/locale/tag' + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/fallbacks.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/fallbacks.rb new file mode 100644 index 0000000..11dcf8c --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/fallbacks.rb @@ -0,0 +1,98 @@ +# encoding: utf-8 + +# Locale Fallbacks +# +# Extends the I18n module to hold a fallbacks instance which is set to an +# instance of I18n::Locale::Fallbacks by default but can be swapped with a +# different implementation. +# +# Locale fallbacks will compute a number of fallback locales for a given locale. +# For example: +# +#

+# I18n.fallbacks[:"es-MX"] # => [:"es-MX", :es, :en] 
+# +# Locale fallbacks always fall back to +# +# * all parent locales of a given locale (e.g. :es for :"es-MX") first, +# * the current default locales and all of their parents second +# +# The default locales are set to [I18n.default_locale] by default but can be +# set to something else. +# +# One can additionally add any number of additional fallback locales manually. +# These will be added before the default locales to the fallback chain. For +# example: +# +# # using the default locale as default fallback locale +# +# I18n.default_locale = :"en-US" +# I18n.fallbacks = I18n::Fallbacks.new(:"de-AT" => :"de-DE") +# I18n.fallbacks[:"de-AT"] # => [:"de-AT", :"de-DE", :de, :"en-US", :en] +# +# # using a custom locale as default fallback locale +# +# I18n.fallbacks = I18n::Fallbacks.new(:"en-GB", :"de-AT" => :de, :"de-CH" => :de) +# I18n.fallbacks[:"de-AT"] # => [:"de-AT", :de, :"en-GB", :en] +# I18n.fallbacks[:"de-CH"] # => [:"de-CH", :de, :"en-GB", :en] +# +# # mapping fallbacks to an existing instance +# +# # people speaking Catalan also speak Spanish as spoken in Spain +# fallbacks = I18n.fallbacks +# fallbacks.map(:ca => :"es-ES") +# fallbacks[:ca] # => [:ca, :"es-ES", :es, :"en-US", :en] +# +# # people speaking Arabian as spoken in Palestine also speak Hebrew as spoken in Israel +# fallbacks.map(:"ar-PS" => :"he-IL") +# fallbacks[:"ar-PS"] # => [:"ar-PS", :ar, :"he-IL", :he, :"en-US", :en] +# fallbacks[:"ar-EG"] # => [:"ar-EG", :ar, :"en-US", :en] +# +# # people speaking Sami as spoken in Finnland also speak Swedish and Finnish as spoken in Finnland +# fallbacks.map(:sms => [:"se-FI", :"fi-FI"]) +# fallbacks[:sms] # => [:sms, :"se-FI", :se, :"fi-FI", :fi, :"en-US", :en] + +module I18n + module Locale + class Fallbacks < Hash + def initialize(*mappings) + @map = {} + map(mappings.pop) if mappings.last.is_a?(Hash) + self.defaults = mappings.empty? ? [I18n.default_locale.to_sym] : mappings + end + + def defaults=(defaults) + @defaults = defaults.map { |default| compute(default, false) }.flatten + end + attr_reader :defaults + + def [](locale) + raise InvalidLocale.new(locale) if locale.nil? + locale = locale.to_sym + super || store(locale, compute(locale)) + end + + def map(mappings) + mappings.each do |from, to| + from, to = from.to_sym, Array(to) + to.each do |to| + @map[from] ||= [] + @map[from] << to.to_sym + end + end + end + + protected + + def compute(tags, include_defaults = true) + result = Array(tags).collect do |tag| + tags = I18n::Locale::Tag.tag(tag).self_and_parents.map! { |t| t.to_sym } + tags.each { |tag| tags += compute(@map[tag]) if @map[tag] } + tags + end.flatten + result.push(*defaults) if include_defaults + result.uniq + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag.rb new file mode 100644 index 0000000..a640b44 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag.rb @@ -0,0 +1,28 @@ +# encoding: utf-8 + +module I18n + module Locale + module Tag + autoload :Parents, 'i18n/locale/tag/parents' + autoload :Rfc4646, 'i18n/locale/tag/rfc4646' + autoload :Simple, 'i18n/locale/tag/simple' + + class << self + # Returns the current locale tag implementation. Defaults to +I18n::Locale::Tag::Simple+. + def implementation + @@implementation ||= Simple + end + + # Sets the current locale tag implementation. Use this to set a different locale tag implementation. + def implementation=(implementation) + @@implementation = implementation + end + + # Factory method for locale tags. Delegates to the current locale tag implementation. + def tag(tag) + implementation.tag(tag) + end + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/parents.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/parents.rb new file mode 100644 index 0000000..a094468 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/parents.rb @@ -0,0 +1,24 @@ +# encoding: utf-8 + +module I18n + module Locale + module Tag + module Parents + def parent + @parent ||= begin + segs = to_a.compact + segs.length > 1 ? self.class.tag(*segs[0..(segs.length-2)].join('-')) : nil + end + end + + def self_and_parents + @self_and_parents ||= [self] + parents + end + + def parents + @parents ||= ([parent] + (parent ? parent.parents : [])).compact + end + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/rfc4646.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/rfc4646.rb new file mode 100644 index 0000000..c20d35a --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/rfc4646.rb @@ -0,0 +1,76 @@ +# encoding: utf-8 + +# RFC 4646/47 compliant Locale tag implementation that parses locale tags to +# subtags such as language, script, region, variant etc. +# +# For more information see by http://en.wikipedia.org/wiki/IETF_language_tag +# +# Rfc4646::Parser does not implement grandfathered tags. + +module I18n + module Locale + module Tag + RFC4646_SUBTAGS = [ :language, :script, :region, :variant, :extension, :privateuse, :grandfathered ] + RFC4646_FORMATS = { :language => :downcase, :script => :capitalize, :region => :upcase, :variant => :downcase } + + class Rfc4646 < Struct.new(*RFC4646_SUBTAGS) + class << self + # Parses the given tag and returns a Tag instance if it is valid. + # Returns false if the given tag is not valid according to RFC 4646. + def tag(tag) + matches = parser.match(tag) + new(*matches) if matches + end + + def parser + @@parser ||= Rfc4646::Parser + end + + def parser=(parser) + @@parser = parser + end + end + + include Parents + + RFC4646_FORMATS.each do |name, format| + define_method(name) { self[name].send(format) unless self[name].nil? } + end + + def to_sym + to_s.to_sym + end + + def to_s + @tag ||= to_a.compact.join("-") + end + + def to_a + members.collect { |attr| self.send(attr) } + end + + module Parser + PATTERN = %r{\A(?: + ([a-z]{2,3}(?:(?:-[a-z]{3}){0,3})?|[a-z]{4}|[a-z]{5,8}) # language + (?:-([a-z]{4}))? # script + (?:-([a-z]{2}|\d{3}))? # region + (?:-([0-9a-z]{5,8}|\d[0-9a-z]{3}))* # variant + (?:-([0-9a-wyz](?:-[0-9a-z]{2,8})+))* # extension + (?:-(x(?:-[0-9a-z]{1,8})+))?| # privateuse subtag + (x(?:-[0-9a-z]{1,8})+)| # privateuse tag + /* ([a-z]{1,3}(?:-[0-9a-z]{2,8}){1,2}) */ # grandfathered + )\z}xi + + class << self + def match(tag) + c = PATTERN.match(tag.to_s).captures + c[0..4] << (c[5].nil? ? c[6] : c[5]) << c[7] # TODO c[7] is grandfathered, throw a NotImplemented exception here? + rescue + false + end + end + end + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/simple.rb new file mode 100644 index 0000000..0fddb36 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/locale/tag/simple.rb @@ -0,0 +1,41 @@ +# encoding: utf-8 + +# Simple Locale tag implementation that computes subtags by simply splitting +# the locale tag at '-' occurences. +module I18n + module Locale + module Tag + class Simple + class << self + def tag(tag) + new(tag) + end + end + + include Parents + + attr_reader :tag + + def initialize(*tag) + @tag = tag.join('-').to_sym + end + + def subtags + @subtags = tag.to_s.split('-').map { |subtag| subtag.to_s } + end + + def to_sym + tag + end + + def to_s + tag.to_s + end + + def to_a + subtags + end + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/version.rb b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/version.rb new file mode 100644 index 0000000..354f07b --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.3.7/i18n/version.rb @@ -0,0 +1,3 @@ +module I18n + VERSION = "0.3.7" +end \ No newline at end of file -- 1.6.4.2 From 2401af4d6f76d0a2ebd8a39ed3e9efa60035cdf3 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 18 Apr 2010 23:01:44 -0700 Subject: [PATCH 593/779] MemoryStore#read_multi(*keys) for dev-mode compatibility with memcache store --- .../lib/active_support/cache/memory_store.rb | 8 +++++++- activesupport/test/caching_test.rb | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletions(-) diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb index 1b30d49..b4a7438 100644 --- a/activesupport/lib/active_support/cache/memory_store.rb +++ b/activesupport/lib/active_support/cache/memory_store.rb @@ -19,6 +19,12 @@ module ActiveSupport @data = {} end + def read_multi(*names) + results = {} + names.each { |n| results[n] = read(n) } + results + end + def read(name, options = nil) super @data[name] @@ -39,7 +45,7 @@ module ActiveSupport @data.delete_if { |k,v| k =~ matcher } end - def exist?(name,options = nil) + def exist?(name, options = nil) super @data.has_key?(name) end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index eed65d4..918ca8b 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -168,6 +168,13 @@ class MemoryStoreTest < ActiveSupport::TestCase assert_raise(ActiveSupport::FrozenObjectError) { @cache.read('foo').gsub!(/.*/, 'baz') } assert_equal 'bar', @cache.read('foo') end + + def test_multi_get + @cache.write('foo', 1) + @cache.write('goo', 2) + result = @cache.read_multi('foo', 'goo') + assert_equal({'foo' => 1, 'goo' => 2}, result) + end end uses_memcached 'memcached backed store' do -- 1.6.4.2 From 49943a712080644969d14ac45919044cb54f174d Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 20 Apr 2010 20:38:06 -0300 Subject: [PATCH 594/779] TimeZones lazy load Signed-off-by: Jeremy Kemper --- .../lib/active_support/values/time_zone.rb | 134 ++++++++++---------- activesupport/test/time_zone_test.rb | 2 +- 2 files changed, 65 insertions(+), 71 deletions(-) diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 73a27ac..368a899 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -304,73 +304,6 @@ module ActiveSupport nil end - unless const_defined?(:ZONES) - ZONES = [] - ZONES_MAP = {} - [[-39_600, "International Date Line West", "Midway Island", "Samoa" ], - [-36_000, "Hawaii" ], - [-32_400, "Alaska" ], - [-28_800, "Pacific Time (US & Canada)", "Tijuana" ], - [-25_200, "Mountain Time (US & Canada)", "Chihuahua", "Mazatlan", - "Arizona" ], - [-21_600, "Central Time (US & Canada)", "Saskatchewan", "Guadalajara", - "Mexico City", "Monterrey", "Central America" ], - [-18_000, "Eastern Time (US & Canada)", "Indiana (East)", "Bogota", - "Lima", "Quito" ], - [-16_200, "Caracas" ], - [-14_400, "Atlantic Time (Canada)", "La Paz", "Santiago" ], - [-12_600, "Newfoundland" ], - [-10_800, "Brasilia", "Buenos Aires", "Georgetown", "Greenland" ], - [ -7_200, "Mid-Atlantic" ], - [ -3_600, "Azores", "Cape Verde Is." ], - [ 0, "Dublin", "Edinburgh", "Lisbon", "London", "Casablanca", - "Monrovia", "UTC" ], - [ 3_600, "Belgrade", "Bratislava", "Budapest", "Ljubljana", "Prague", - "Sarajevo", "Skopje", "Warsaw", "Zagreb", "Brussels", - "Copenhagen", "Madrid", "Paris", "Amsterdam", "Berlin", - "Bern", "Rome", "Stockholm", "Vienna", - "West Central Africa" ], - [ 7_200, "Bucharest", "Cairo", "Helsinki", "Kyev", "Riga", "Sofia", - "Tallinn", "Vilnius", "Athens", "Istanbul", "Minsk", - "Jerusalem", "Harare", "Pretoria" ], - [ 10_800, "Moscow", "St. Petersburg", "Volgograd", "Kuwait", "Riyadh", - "Nairobi", "Baghdad" ], - [ 12_600, "Tehran" ], - [ 14_400, "Abu Dhabi", "Muscat", "Baku", "Tbilisi", "Yerevan" ], - [ 16_200, "Kabul" ], - [ 18_000, "Ekaterinburg", "Islamabad", "Karachi", "Tashkent" ], - [ 19_800, "Chennai", "Kolkata", "Mumbai", "New Delhi", "Sri Jayawardenepura" ], - [ 20_700, "Kathmandu" ], - [ 21_600, "Astana", "Dhaka", "Almaty", - "Novosibirsk" ], - [ 23_400, "Rangoon" ], - [ 25_200, "Bangkok", "Hanoi", "Jakarta", "Krasnoyarsk" ], - [ 28_800, "Beijing", "Chongqing", "Hong Kong", "Urumqi", - "Kuala Lumpur", "Singapore", "Taipei", "Perth", "Irkutsk", - "Ulaan Bataar" ], - [ 32_400, "Seoul", "Osaka", "Sapporo", "Tokyo", "Yakutsk" ], - [ 34_200, "Darwin", "Adelaide" ], - [ 36_000, "Canberra", "Melbourne", "Sydney", "Brisbane", "Hobart", - "Vladivostok", "Guam", "Port Moresby" ], - [ 39_600, "Magadan", "Solomon Is.", "New Caledonia" ], - [ 43_200, "Fiji", "Kamchatka", "Marshall Is.", "Auckland", - "Wellington" ], - [ 46_800, "Nuku'alofa" ]]. - each do |offset, *places| - places.each do |place| - place.freeze - zone = new(place, offset) - ZONES << zone - ZONES_MAP[place] = zone - end - end - ZONES.sort! - ZONES.freeze - - US_ZONES = ZONES.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ } - US_ZONES.freeze - end - class << self alias_method :create, :new @@ -385,7 +318,68 @@ module ActiveSupport # TimeZone objects per time zone, in many cases, to make it easier # for users to find their own time zone. def all - ZONES + @zones ||= zones_map.values.sort + end + + def zones_map + unless defined?(@zones_map) + @zones_map = {} + [[-39_600, "International Date Line West", "Midway Island", "Samoa" ], + [-36_000, "Hawaii" ], + [-32_400, "Alaska" ], + [-28_800, "Pacific Time (US & Canada)", "Tijuana" ], + [-25_200, "Mountain Time (US & Canada)", "Chihuahua", "Mazatlan", + "Arizona" ], + [-21_600, "Central Time (US & Canada)", "Saskatchewan", "Guadalajara", + "Mexico City", "Monterrey", "Central America" ], + [-18_000, "Eastern Time (US & Canada)", "Indiana (East)", "Bogota", + "Lima", "Quito" ], + [-16_200, "Caracas" ], + [-14_400, "Atlantic Time (Canada)", "La Paz", "Santiago" ], + [-12_600, "Newfoundland" ], + [-10_800, "Brasilia", "Buenos Aires", "Georgetown", "Greenland" ], + [ -7_200, "Mid-Atlantic" ], + [ -3_600, "Azores", "Cape Verde Is." ], + [ 0, "Dublin", "Edinburgh", "Lisbon", "London", "Casablanca", + "Monrovia", "UTC" ], + [ 3_600, "Belgrade", "Bratislava", "Budapest", "Ljubljana", "Prague", + "Sarajevo", "Skopje", "Warsaw", "Zagreb", "Brussels", + "Copenhagen", "Madrid", "Paris", "Amsterdam", "Berlin", + "Bern", "Rome", "Stockholm", "Vienna", + "West Central Africa" ], + [ 7_200, "Bucharest", "Cairo", "Helsinki", "Kyev", "Riga", "Sofia", + "Tallinn", "Vilnius", "Athens", "Istanbul", "Minsk", + "Jerusalem", "Harare", "Pretoria" ], + [ 10_800, "Moscow", "St. Petersburg", "Volgograd", "Kuwait", "Riyadh", + "Nairobi", "Baghdad" ], + [ 12_600, "Tehran" ], + [ 14_400, "Abu Dhabi", "Muscat", "Baku", "Tbilisi", "Yerevan" ], + [ 16_200, "Kabul" ], + [ 18_000, "Ekaterinburg", "Islamabad", "Karachi", "Tashkent" ], + [ 19_800, "Chennai", "Kolkata", "Mumbai", "New Delhi", "Sri Jayawardenepura" ], + [ 20_700, "Kathmandu" ], + [ 21_600, "Astana", "Dhaka", "Almaty", + "Novosibirsk" ], + [ 23_400, "Rangoon" ], + [ 25_200, "Bangkok", "Hanoi", "Jakarta", "Krasnoyarsk" ], + [ 28_800, "Beijing", "Chongqing", "Hong Kong", "Urumqi", + "Kuala Lumpur", "Singapore", "Taipei", "Perth", "Irkutsk", + "Ulaan Bataar" ], + [ 32_400, "Seoul", "Osaka", "Sapporo", "Tokyo", "Yakutsk" ], + [ 34_200, "Darwin", "Adelaide" ], + [ 36_000, "Canberra", "Melbourne", "Sydney", "Brisbane", "Hobart", + "Vladivostok", "Guam", "Port Moresby" ], + [ 39_600, "Magadan", "Solomon Is.", "New Caledonia" ], + [ 43_200, "Fiji", "Kamchatka", "Marshall Is.", "Auckland", + "Wellington" ], + [ 46_800, "Nuku'alofa" ]]. + each do |offset, *places| + places.each do |place| + @zones_map[place] = create(place, offset) + end + end + end + @zones_map end # Locate a specific time zone object. If the argument is a string, it @@ -396,7 +390,7 @@ module ActiveSupport def [](arg) case arg when String - ZONES_MAP[arg] ||= lookup(arg) + zones_map[arg] ||= lookup(arg) when Numeric, ActiveSupport::Duration arg *= 3600 if arg.abs <= 13 all.find { |z| z.utc_offset == arg.to_i } @@ -408,7 +402,7 @@ module ActiveSupport # A convenience method for returning a collection of TimeZone objects # for time zones in the USA. def us_zones - US_ZONES + @us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ } end private diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 2802ffd..41d203d 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -40,7 +40,7 @@ class TimeZoneTest < Test::Unit::TestCase end define_method("test_zones_map_for_#{name}") do - assert_equal ActiveSupport::TimeZone::ZONES_MAP[zone.name], zone + assert_equal ActiveSupport::TimeZone.zones_map[zone.name], zone end end -- 1.6.4.2 From d891754535908def1e6777d9bc01ccbe74d70335 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 21 Apr 2010 01:47:23 -0300 Subject: [PATCH 595/779] load tzinfo on initialize only Signed-off-by: Xavier Noria --- .../lib/active_support/values/time_zone.rb | 7 ++----- 1 files changed, 2 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 368a899..52f0f0f 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -172,6 +172,7 @@ module ActiveSupport include Comparable attr_reader :name + attr_reader :tzinfo # Create a new TimeZone object with the given name and offset. The # offset is the number of seconds that this time zone is offset from UTC @@ -180,7 +181,7 @@ module ActiveSupport def initialize(name, utc_offset = nil, tzinfo = nil) @name = name @utc_offset = utc_offset - @tzinfo = tzinfo + @tzinfo = tzinfo || TimeZone.find_tzinfo(name) @current_period = nil end @@ -292,10 +293,6 @@ module ActiveSupport tzinfo.period_for_local(time, dst) end - def tzinfo - @tzinfo ||= TimeZone.find_tzinfo(name) - end - # TODO: Preload instead of lazy load for thread safety def self.find_tzinfo(name) require 'tzinfo' unless defined?(TZInfo) -- 1.6.4.2 From c401102a2702f9b945803e66d3a25b77d882ee13 Mon Sep 17 00:00:00 2001 From: Sam Elliott and Santiago Pastorino Date: Thu, 22 Apr 2010 18:01:20 -0300 Subject: [PATCH 596/779] Is not nessesary to have @_rails_html_safe instance var when the string is unsafe, also it breaks to_yaml [#3535 state:committed] --- .../core_ext/string/output_safety.rb | 4 ++-- activesupport/test/core_ext/string_ext_test.rb | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 95ed5d8..1d779a0 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -123,7 +123,7 @@ class String end def html_safe? - defined?(@_rails_html_safe) && @_rails_html_safe + defined?(@_rails_html_safe) end def html_safe! @@ -145,7 +145,7 @@ class String def concat_with_safety(other_or_fixnum) result = concat_without_safety(other_or_fixnum) unless html_safe? && also_html_safe?(other_or_fixnum) - @_rails_html_safe = false + remove_instance_variable(:@_rails_html_safe) if defined?(@_rails_html_safe) end result end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 084f861..af3ec12 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -505,4 +505,12 @@ class OutputSafetyTest < ActiveSupport::TestCase test 'emits normal string yaml' do assert_equal 'foo'.to_yaml, 'foo'.html_safe.to_yaml(:foo => 1) end + + test 'yaml output using +' do + assert_equal "--- foobar\n", ('foo' + 'bar').to_yaml + end + + test 'yaml output using <<' do + assert_equal "--- foobar\n", ('foo' << 'bar').to_yaml + end end -- 1.6.4.2 From cec44f58388755dec8d4e739e0a3bc3263a7fe3f Mon Sep 17 00:00:00 2001 From: Eugene Pimenov Date: Sun, 20 Sep 2009 14:35:24 +0400 Subject: [PATCH 597/779] PostgreSQL adapter: escape_bytea, quote_string and unescape_bytea aren't thread-safe in Ruby 1.8 [#3237 state:resolved] Signed-off-by: wycats --- .../connection_adapters/postgresql_adapter.rb | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index bc289ff..84fae94 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -298,7 +298,7 @@ module ActiveRecord # QUOTING ================================================== # Escapes binary strings for bytea input to the database. - def escape_bytea(value) + def escape_bytea(original_value) if @connection.respond_to?(:escape_bytea) self.class.instance_eval do define_method(:escape_bytea) do |value| @@ -322,13 +322,13 @@ module ActiveRecord end end end - escape_bytea(value) + escape_bytea(original_value) end # Unescapes bytea output from a database to the binary string it represents. # NOTE: This is NOT an inverse of escape_bytea! This is only to be used # on escaped binary output from database drive. - def unescape_bytea(value) + def unescape_bytea(original_value) # In each case, check if the value actually is escaped PostgreSQL bytea output # or an unescaped Active Record attribute that was just written. if PGconn.respond_to?(:unescape_bytea) @@ -368,7 +368,7 @@ module ActiveRecord end end end - unescape_bytea(value) + unescape_bytea(original_value) end # Quotes PostgreSQL-specific data types for SQL input. @@ -393,7 +393,7 @@ module ActiveRecord end # Quotes strings for use in SQL input in the postgres driver for better performance. - def quote_string(s) #:nodoc: + def quote_string(original_value) #:nodoc: if @connection.respond_to?(:escape) self.class.instance_eval do define_method(:quote_string) do |s| @@ -413,7 +413,7 @@ module ActiveRecord remove_method(:quote_string) end end - quote_string(s) + quote_string(original_value) end # Checks the following cases: -- 1.6.4.2 From c9e15709aef6baed329157adeef58099f16501b2 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 24 Apr 2010 15:27:18 -0700 Subject: [PATCH 598/779] PostgreSQL: use standard-conforming strings if possible --- .../connection_adapters/postgresql_adapter.rb | 68 ++++++-------------- 1 files changed, 19 insertions(+), 49 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 84fae94..516ed0c 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -261,20 +261,12 @@ module ActiveRecord true end - # Does PostgreSQL support standard conforming strings? - def supports_standard_conforming_strings? - # Temporarily set the client message level above error to prevent unintentional - # error messages in the logs when working on a PostgreSQL database server that - # does not support standard conforming strings. - client_min_messages_old = client_min_messages - self.client_min_messages = 'panic' - - # postgres-pr does not raise an exception when client_min_messages is set higher - # than error and "SHOW standard_conforming_strings" fails, but returns an empty - # PGresult instead. - has_support = query('SHOW standard_conforming_strings')[0][0] rescue false - self.client_min_messages = client_min_messages_old - has_support + # Enable standard-conforming strings if available. + def set_standard_conforming_strings + old, self.client_min_messages = client_min_messages, 'panic' + execute('SET standard_conforming_strings = on') rescue nil + ensure + self.client_min_messages = old end def supports_insert_with_returning? @@ -331,42 +323,28 @@ module ActiveRecord def unescape_bytea(original_value) # In each case, check if the value actually is escaped PostgreSQL bytea output # or an unescaped Active Record attribute that was just written. - if PGconn.respond_to?(:unescape_bytea) + if @connection.respond_to?(:unescape_bytea) self.class.instance_eval do define_method(:unescape_bytea) do |value| if value =~ /\\\d{3}/ - PGconn.unescape_bytea(value) + @connection.unescape_bytea(value) else value end end end - else + elsif PGconn.respond_to?(:unescape_bytea) self.class.instance_eval do define_method(:unescape_bytea) do |value| if value =~ /\\\d{3}/ - result = '' - i, max = 0, value.size - while i < max - char = value[i] - if char == ?\\ - if value[i+1] == ?\\ - char = ?\\ - i += 1 - else - char = value[i+1..i+3].oct - i += 3 - end - end - result << char - i += 1 - end - result + PGconn.unescape_bytea(value) else value end end end + else + raise 'Your PostgreSQL connection does not support unescape_bytea. Try upgrading to pg 0.9.0 or later.' end unescape_bytea(original_value) end @@ -374,10 +352,10 @@ module ActiveRecord # Quotes PostgreSQL-specific data types for SQL input. def quote(value, column = nil) #:nodoc: if value.kind_of?(String) && column && column.type == :binary - "#{quoted_string_prefix}'#{escape_bytea(value)}'" - elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/ - "xml E'#{quote_string(value)}'" - elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/ + "'#{escape_bytea(value)}'" + elsif value.kind_of?(String) && column && column.sql_type == 'xml' + "xml '#{quote_string(value)}'" + elsif value.kind_of?(Numeric) && column && column.sql_type == 'money' # Not truly string input, so doesn't require (or allow) escape string syntax. "'#{value.to_s}'" elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/ @@ -971,17 +949,6 @@ module ActiveRecord # Ignore async_exec and async_query when using postgres-pr. @async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec) - # Use escape string syntax if available. We cannot do this lazily when encountering - # the first string, because that could then break any transactions in progress. - # See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html - # If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't - # support escape string syntax. Don't override the inherited quoted_string_prefix. - if supports_standard_conforming_strings? - self.class.instance_eval do - define_method(:quoted_string_prefix) { 'E' } - end - end - # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision # should know about this but can't detect it there, so deal with it here. @@ -1011,6 +978,9 @@ module ActiveRecord end self.client_min_messages = @config[:min_messages] if @config[:min_messages] self.schema_search_path = @config[:schema_search_path] || @config[:schema_order] + + # Use standard-conforming strings if available so we don't have to do the E'...' dance. + set_standard_conforming_strings end # Returns the current ID of a table's sequence. -- 1.6.4.2 From 5c0ad822363f15d372caaee8b39f3c0e89a13503 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 24 Apr 2010 15:51:59 -0700 Subject: [PATCH 599/779] PostgreSQL: remove the unnecessary heuristic checking whether the value is escaped --- .../connection_adapters/postgresql_adapter.rb | 12 ++---------- 1 files changed, 2 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 516ed0c..87b2ef6 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -326,21 +326,13 @@ module ActiveRecord if @connection.respond_to?(:unescape_bytea) self.class.instance_eval do define_method(:unescape_bytea) do |value| - if value =~ /\\\d{3}/ - @connection.unescape_bytea(value) - else - value - end + @connection.unescape_bytea(value) if value end end elsif PGconn.respond_to?(:unescape_bytea) self.class.instance_eval do define_method(:unescape_bytea) do |value| - if value =~ /\\\d{3}/ - PGconn.unescape_bytea(value) - else - value - end + PGconn.unescape_bytea(value) if value end end else -- 1.6.4.2 From 457a54653d83406f4fe9105416034bbde8f2e637 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 24 Apr 2010 16:27:20 -0700 Subject: [PATCH 600/779] Remove quoted_string_prefix entirely since PostgreSQL was the only database adapter relying on it. --- .../connection_adapters/abstract/quoting.rb | 10 +++------- activerecord/test/cases/finder_test.rb | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index 3a7bf35..50a1f70 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -11,12 +11,12 @@ module ActiveRecord when String, ActiveSupport::Multibyte::Chars value = value.to_s if column && column.type == :binary && column.class.respond_to?(:string_to_binary) - "#{quoted_string_prefix}'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode) + "'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode) elsif column && [:integer, :float].include?(column.type) value = column.type == :integer ? value.to_i : value.to_f value.to_s else - "#{quoted_string_prefix}'#{quote_string(value)}'" # ' (for ruby-mode) + "'#{quote_string(value)}'" # ' (for ruby-mode) end when NilClass then "NULL" when TrueClass then (column && column.type == :integer ? '1' : quoted_true) @@ -28,7 +28,7 @@ module ActiveRecord if value.acts_like?(:date) || value.acts_like?(:time) "'#{quoted_date(value)}'" else - "#{quoted_string_prefix}'#{quote_string(value.to_yaml)}'" + "'#{quote_string(value.to_yaml)}'" end end end @@ -60,10 +60,6 @@ module ActiveRecord def quoted_date(value) value.to_s(:db) end - - def quoted_string_prefix - '' - end end end end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index f1bede9..0959585 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -518,8 +518,8 @@ class FinderTest < ActiveRecord::TestCase end def test_string_sanitation - assert_not_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1") - assert_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something; select table'", ActiveRecord::Base.sanitize("something; select table") + assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1") + assert_equal "'something; select table'", ActiveRecord::Base.sanitize("something; select table") end def test_count -- 1.6.4.2 From 81e06075b707ac07a65701fcd667ffc93580e736 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 28 May 2009 17:12:28 -0500 Subject: [PATCH 601/779] Ruby 1.9: ERB template encoding using a magic comment at the top of the file --- actionpack/CHANGELOG | 5 ++++- actionpack/lib/action_view/renderable.rb | 14 ++++++++++++-- .../lib/action_view/template_handlers/erb.rb | 6 +----- actionpack/test/fixtures/test/utf8.html.erb | 5 ++++- actionpack/test/template/render_test.rb | 2 +- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 4852d70..1e02b04 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,4 +1,7 @@ -*Edge* +*2.3.6 (unreleased)* + +* Ruby 1.9: ERB template encoding using a magic comment at the top of the file. [Jeremy Kemper] + <%# encoding: utf-8 %> * Fixed that default locale templates should be used if the current locale template is missing [DHH] diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index ff7bc7d..c8c974e 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -65,16 +65,26 @@ module ActionView def compile!(render_symbol, local_assigns) locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join + code = compiled_source + encoding_comment = $1 if code.sub!(/\A(#.*coding.*)\n/, '') + source = <<-end_src def #{render_symbol}(local_assigns) - old_output_buffer = output_buffer;#{locals_code};#{compiled_source} + old_output_buffer = output_buffer;#{locals_code};#{code} ensure self.output_buffer = old_output_buffer end end_src + if encoding_comment + source = "#{encoding_comment}\n#{source}" + line = -1 + else + line = 0 + end + begin - ActionView::Base::CompiledTemplates.module_eval(source, filename, 0) + ActionView::Base::CompiledTemplates.module_eval(source, filename, line) rescue Errno::ENOENT => e raise e # Missing template file, re-raise for Base to rescue rescue Exception => e # errors from template code diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb index e3120ba..2bf87ba 100644 --- a/actionpack/lib/action_view/template_handlers/erb.rb +++ b/actionpack/lib/action_view/template_handlers/erb.rb @@ -11,11 +11,7 @@ module ActionView self.erb_trim_mode = '-' def compile(template) - src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src - - # Ruby 1.9 prepends an encoding to the source. However this is - # useless because you can only set an encoding on the first line - RUBY_VERSION >= '1.9' ? src.sub(/\A#coding:.*\n/, '') : src + ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src end end end diff --git a/actionpack/test/fixtures/test/utf8.html.erb b/actionpack/test/fixtures/test/utf8.html.erb index 0b4d19a..58cd03b 100644 --- a/actionpack/test/fixtures/test/utf8.html.erb +++ b/actionpack/test/fixtures/test/utf8.html.erb @@ -1,2 +1,5 @@ +<%# encoding: utf-8 -%> Русский текст -日本語のテキスト \ No newline at end of file +<%= "日".encoding %> +<%= @output_buffer.encoding %> +<%= __ENCODING__ %> diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 9adf053..ff7e915 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -251,7 +251,7 @@ module RenderTestCases if '1.9'.respond_to?(:force_encoding) def test_render_utf8_template result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") - assert_equal "Русский текст\n日本語のテキスト", result + assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result assert_equal Encoding::UTF_8, result.encoding end end -- 1.6.4.2 From 70034d820f77d53fc546692276225ae55c4ee402 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 1 Jun 2009 12:33:49 -0700 Subject: [PATCH 602/779] Ensure ERB source begins with the encoding comment --- actionpack/lib/action_view/renderable.rb | 6 ++++- .../lib/action_view/template_handlers/erb.rb | 4 ++- actionpack/test/fixtures/test/utf8.html.erb | 1 - actionpack/test/fixtures/test/utf8_magic.html.erb | 5 ++++ actionpack/test/template/render_test.rb | 25 ++++++++++++++++--- 5 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 actionpack/test/fixtures/test/utf8_magic.html.erb diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index c8c974e..a7f87ee 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -66,7 +66,11 @@ module ActionView locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join code = compiled_source - encoding_comment = $1 if code.sub!(/\A(#.*coding.*)\n/, '') + if code.sub!(/\A(#.*coding.*)\n/, '') + encoding_comment = $1 + elsif defined?(Encoding) && Encoding.respond_to?(:default_external) + encoding_comment = "#coding:#{Encoding.default_external}" + end source = <<-end_src def #{render_symbol}(local_assigns) diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb index 2bf87ba..978a577 100644 --- a/actionpack/lib/action_view/template_handlers/erb.rb +++ b/actionpack/lib/action_view/template_handlers/erb.rb @@ -11,7 +11,9 @@ module ActionView self.erb_trim_mode = '-' def compile(template) - ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src + magic = $1 if template.source =~ /\A(<%#.*coding:\s*(\S+)\s*-?%>)/ + erb = "#{magic}<% __in_erb_template=true %>#{template.source}" + ::ERB.new(erb, nil, erb_trim_mode, '@output_buffer').src end end end diff --git a/actionpack/test/fixtures/test/utf8.html.erb b/actionpack/test/fixtures/test/utf8.html.erb index 58cd03b..14fe12d 100644 --- a/actionpack/test/fixtures/test/utf8.html.erb +++ b/actionpack/test/fixtures/test/utf8.html.erb @@ -1,4 +1,3 @@ -<%# encoding: utf-8 -%> Русский текст <%= "日".encoding %> <%= @output_buffer.encoding %> diff --git a/actionpack/test/fixtures/test/utf8_magic.html.erb b/actionpack/test/fixtures/test/utf8_magic.html.erb new file mode 100644 index 0000000..58cd03b --- /dev/null +++ b/actionpack/test/fixtures/test/utf8_magic.html.erb @@ -0,0 +1,5 @@ +<%# encoding: utf-8 -%> +Русский текст +<%= "日".encoding %> +<%= @output_buffer.encoding %> +<%= __ENCODING__ %> diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index ff7e915..ffadc9a 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -249,10 +249,27 @@ module RenderTestCases end if '1.9'.respond_to?(:force_encoding) - def test_render_utf8_template - result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") - assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result - assert_equal Encoding::UTF_8, result.encoding + def test_render_utf8_template_with_magic_comment + with_external_encoding Encoding::ASCII_8BIT do + result = @view.render(:file => "test/utf8_magic.html.erb", :layouts => "layouts/yield") + assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result + assert_equal Encoding::UTF_8, result.encoding + end + end + + def test_render_utf8_template_with_default_external_encoding + with_external_encoding Encoding::UTF_8 do + result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") + assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result + assert_equal Encoding::UTF_8, result.encoding + end + end + + def with_external_encoding(encoding) + old, Encoding.default_external = Encoding.default_external, encoding + yield + ensure + Encoding.default_external = old end end end -- 1.6.4.2 From 4082001331e8d405e534a47eb8868ca6d9c228c2 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Wed, 1 Jul 2009 16:25:33 -0700 Subject: [PATCH 603/779] Fix pattern to match various magic comment formats --- .../lib/action_view/template_handlers/erb.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb index 978a577..407de4b 100644 --- a/actionpack/lib/action_view/template_handlers/erb.rb +++ b/actionpack/lib/action_view/template_handlers/erb.rb @@ -11,7 +11,7 @@ module ActionView self.erb_trim_mode = '-' def compile(template) - magic = $1 if template.source =~ /\A(<%#.*coding:\s*(\S+)\s*-?%>)/ + magic = $1 if template.source =~ /\A(<%#.*coding[:=]\s*(\S+)\s*-?%>)/ erb = "#{magic}<% __in_erb_template=true %>#{template.source}" ::ERB.new(erb, nil, erb_trim_mode, '@output_buffer').src end -- 1.6.4.2 From fb545f4c60ea5e22a583dce9febbf154cd2f9f8c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 8 Apr 2010 19:27:22 -0700 Subject: [PATCH 604/779] Expect an incompatible encoding exception when a template doesn't have a magic comment and its source encoding doesn't match the default external encoding --- actionpack/test/fixtures/test/utf8.html.erb | 2 +- actionpack/test/fixtures/test/utf8_magic.html.erb | 2 +- actionpack/test/template/render_test.rb | 61 ++++++++++++--------- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/actionpack/test/fixtures/test/utf8.html.erb b/actionpack/test/fixtures/test/utf8.html.erb index 14fe12d..551c24e 100644 --- a/actionpack/test/fixtures/test/utf8.html.erb +++ b/actionpack/test/fixtures/test/utf8.html.erb @@ -1,4 +1,4 @@ -Русский текст +Русский <%= "текст" %> <%= "日".encoding %> <%= @output_buffer.encoding %> <%= __ENCODING__ %> diff --git a/actionpack/test/fixtures/test/utf8_magic.html.erb b/actionpack/test/fixtures/test/utf8_magic.html.erb index 58cd03b..e2aa85e 100644 --- a/actionpack/test/fixtures/test/utf8_magic.html.erb +++ b/actionpack/test/fixtures/test/utf8_magic.html.erb @@ -1,5 +1,5 @@ <%# encoding: utf-8 -%> -Русский текст +Русский <%= "текст" %> <%= "日".encoding %> <%= @output_buffer.encoding %> <%= __ENCODING__ %> diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index ffadc9a..353f1ba 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -247,31 +247,6 @@ module RenderTestCases assert_equal %(title\n
column
\n
content
\n), @view.render(:file => "test/nested_layout.erb", :layout => "layouts/yield") end - - if '1.9'.respond_to?(:force_encoding) - def test_render_utf8_template_with_magic_comment - with_external_encoding Encoding::ASCII_8BIT do - result = @view.render(:file => "test/utf8_magic.html.erb", :layouts => "layouts/yield") - assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result - assert_equal Encoding::UTF_8, result.encoding - end - end - - def test_render_utf8_template_with_default_external_encoding - with_external_encoding Encoding::UTF_8 do - result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") - assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result - assert_equal Encoding::UTF_8, result.encoding - end - end - - def with_external_encoding(encoding) - old, Encoding.default_external = Encoding.default_external, encoding - yield - ensure - Encoding.default_external = old - end - end end module TemplatesSetupTeardown @@ -303,5 +278,41 @@ class ReloadableRenderTest < Test::Unit::TestCase def setup setup_view_paths_for(cache_templates = false) end + + if '1.9'.respond_to?(:force_encoding) + def test_render_utf8_template_with_magic_comment + with_external_encoding Encoding::ASCII_8BIT do + result = @view.render(:file => "test/utf8_magic.html.erb", :layouts => "layouts/yield") + assert_equal Encoding::UTF_8, result.encoding + assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result + end + end + + def test_render_utf8_template_with_default_external_encoding + with_external_encoding Encoding::UTF_8 do + result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") + assert_equal Encoding::UTF_8, result.encoding + assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result + end + end + + def test_render_utf8_template_with_incompatible_external_encoding + with_external_encoding Encoding::SJIS do + begin + result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") + flunk 'Should have raised incompatible encoding error' + rescue ActionView::Template::Error => error + assert_match 'invalid byte sequence in Shift_JIS', error.original_exception.message + end + end + end + + def with_external_encoding(encoding) + old, Encoding.default_external = Encoding.default_external, encoding + yield + ensure + Encoding.default_external = old + end + end end -- 1.6.4.2 From f6e71c674c3d98c968f2186169120ab042b173bd Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 8 Apr 2010 20:22:07 -0700 Subject: [PATCH 605/779] Expect an incompatible encoding exception when a template with a magic comment renders a partial without one and its source encoding doesn't match the default external encoding --- .../test/fixtures/test/_utf8_partial.html.erb | 1 + .../fixtures/test/_utf8_partial_magic.html.erb | 2 ++ actionpack/test/fixtures/test/utf8.html.erb | 2 +- actionpack/test/fixtures/test/utf8_magic.html.erb | 2 +- .../test/utf8_magic_with_bare_partial.html.erb | 5 +++++ actionpack/test/template/render_test.rb | 15 +++++++++++++-- 6 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 actionpack/test/fixtures/test/_utf8_partial.html.erb create mode 100644 actionpack/test/fixtures/test/_utf8_partial_magic.html.erb create mode 100644 actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb diff --git a/actionpack/test/fixtures/test/_utf8_partial.html.erb b/actionpack/test/fixtures/test/_utf8_partial.html.erb new file mode 100644 index 0000000..8d717fd --- /dev/null +++ b/actionpack/test/fixtures/test/_utf8_partial.html.erb @@ -0,0 +1 @@ +<%= "текст" %> diff --git a/actionpack/test/fixtures/test/_utf8_partial_magic.html.erb b/actionpack/test/fixtures/test/_utf8_partial_magic.html.erb new file mode 100644 index 0000000..4e22246 --- /dev/null +++ b/actionpack/test/fixtures/test/_utf8_partial_magic.html.erb @@ -0,0 +1,2 @@ +<%# encoding: utf-8 -%> +<%= "текст" %> diff --git a/actionpack/test/fixtures/test/utf8.html.erb b/actionpack/test/fixtures/test/utf8.html.erb index 551c24e..ac98c2f 100644 --- a/actionpack/test/fixtures/test/utf8.html.erb +++ b/actionpack/test/fixtures/test/utf8.html.erb @@ -1,4 +1,4 @@ -Русский <%= "текст" %> +Русский <%= render :partial => 'test/utf8_partial' %> <%= "日".encoding %> <%= @output_buffer.encoding %> <%= __ENCODING__ %> diff --git a/actionpack/test/fixtures/test/utf8_magic.html.erb b/actionpack/test/fixtures/test/utf8_magic.html.erb index e2aa85e..257279c 100644 --- a/actionpack/test/fixtures/test/utf8_magic.html.erb +++ b/actionpack/test/fixtures/test/utf8_magic.html.erb @@ -1,5 +1,5 @@ <%# encoding: utf-8 -%> -Русский <%= "текст" %> +Русский <%= render :partial => 'test/utf8_partial_magic' %> <%= "日".encoding %> <%= @output_buffer.encoding %> <%= __ENCODING__ %> diff --git a/actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb b/actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb new file mode 100644 index 0000000..cb22692 --- /dev/null +++ b/actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb @@ -0,0 +1,5 @@ +<%# encoding: utf-8 -%> +Русский <%= render :partial => 'test/utf8_partial' %> +<%= "日".encoding %> +<%= @output_buffer.encoding %> +<%= __ENCODING__ %> diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 353f1ba..4e668d1 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -284,7 +284,7 @@ class ReloadableRenderTest < Test::Unit::TestCase with_external_encoding Encoding::ASCII_8BIT do result = @view.render(:file => "test/utf8_magic.html.erb", :layouts => "layouts/yield") assert_equal Encoding::UTF_8, result.encoding - assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result + assert_equal "Русский текст\n\nUTF-8\nUTF-8\nUTF-8\n", result end end @@ -292,7 +292,7 @@ class ReloadableRenderTest < Test::Unit::TestCase with_external_encoding Encoding::UTF_8 do result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") assert_equal Encoding::UTF_8, result.encoding - assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result + assert_equal "Русский текст\n\nUTF-8\nUTF-8\nUTF-8\n", result end end @@ -307,6 +307,17 @@ class ReloadableRenderTest < Test::Unit::TestCase end end + def test_render_utf8_template_with_partial_with_incompatible_encoding + with_external_encoding Encoding::SJIS do + begin + result = @view.render(:file => "test/utf8_magic_with_bare_partial.html.erb", :layouts => "layouts/yield") + flunk 'Should have raised incompatible encoding error' + rescue ActionView::Template::Error => error + assert_match 'invalid byte sequence in Shift_JIS', error.original_exception.message + end + end + end + def with_external_encoding(encoding) old, Encoding.default_external = Encoding.default_external, encoding yield -- 1.6.4.2 From ec7716abcdaa79082a32ee087391eac31859a522 Mon Sep 17 00:00:00 2001 From: Cezary Baginski Date: Sat, 24 Apr 2010 12:02:08 +0200 Subject: [PATCH 606/779] actionpack: added missing encoding comments [#4466 state:resolved] Signed-off-by: Jeremy Kemper --- actionmailer/lib/action_mailer/quoting.rb | 1 + .../vendor/tmail-1.2.7/tmail/scanner_r.rb | 3 +- .../vendor/tmail-1.2.7/tmail/utils.rb | 55 ++++++++++---------- .../rchardet-1.3/lib/rchardet/universaldetector.rb | 23 ++++---- actionpack/test/controller/assert_select_test.rb | 1 + actionpack/test/controller/routing_test.rb | 1 + actionpack/test/template/text_helper_test.rb | 1 + 7 files changed, 46 insertions(+), 39 deletions(-) diff --git a/actionmailer/lib/action_mailer/quoting.rb b/actionmailer/lib/action_mailer/quoting.rb index 94fa042..5a32b65 100644 --- a/actionmailer/lib/action_mailer/quoting.rb +++ b/actionmailer/lib/action_mailer/quoting.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii module ActionMailer module Quoting #:nodoc: # Convert the given text into quoted printable format, with an instruction diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner_r.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner_r.rb index f207550..5ed8256 100644 --- a/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner_r.rb +++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/scanner_r.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii # scanner_r.rb # #-- @@ -258,4 +259,4 @@ module TMail end end # module TMail -#:startdoc: \ No newline at end of file +#:startdoc: diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/utils.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/utils.rb index 68e5898..3590ca2 100644 --- a/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/utils.rb +++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/utils.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii #-- # Copyright (c) 1998-2003 Minero Aoki # @@ -25,29 +26,29 @@ #++ # = TMail - The EMail Swiss Army Knife for Ruby -# +# # The TMail library provides you with a very complete way to handle and manipulate EMails # from within your Ruby programs. -# +# # Used as the backbone for email handling by the Ruby on Rails and Nitro web frameworks as # well as a bunch of other Ruby apps including the Ruby-Talk mailing list to newsgroup email # gateway, it is a proven and reliable email handler that won't let you down. -# +# # Originally created by Minero Aoki, TMail has been recently picked up by Mikel Lindsaar and # is being actively maintained. Numerous backlogged bug fixes have been applied as well as # Ruby 1.9 compatibility and a swath of documentation to boot. -# +# # TMail allows you to treat an email totally as an object and allow you to get on with your # own programming without having to worry about crafting the perfect email address validation # parser, or assembling an email from all it's component parts. -# +# # TMail handles the most complex part of the email - the header. It generates and parses # headers and provides you with instant access to their innards through simple and logically # named accessor and setter methods. -# +# # TMail also provides a wrapper to Net/SMTP as well as Unix Mailbox handling methods to # directly read emails from your unix mailbox, parse them and use them. -# +# # Following is the comprehensive list of methods to access TMail::Mail objects. You can also # check out TMail::Mail, TMail::Address and TMail::Headers for other lists. module TMail @@ -57,9 +58,9 @@ module TMail # Provides a new email boundary to separate parts of the email. This is a random # string based off the current time, so should be fairly unique. - # + # # For Example: - # + # # TMail.new_boundary # #=> "mimepart_47bf656968207_25a8fbb80114" # TMail.new_boundary @@ -70,12 +71,12 @@ module TMail # Provides a new email message ID. You can use this to generate unique email message # id's for your email so you can track them. - # - # Optionally takes a fully qualified domain name (default to the current hostname + # + # Optionally takes a fully qualified domain name (default to the current hostname # returned by Socket.gethostname) that will be appended to the message ID. - # + # # For Example: - # + # # email.message_id = TMail.new_message_id # #=> "<47bf66845380e_25a8fbb80332@baci.local.tmail>" # email.to_s @@ -102,10 +103,10 @@ module TMail @uniq = 0 #:startdoc: - + # Text Utils provides a namespace to define TOKENs, ATOMs, PHRASEs and CONTROL characters that # are OK per RFC 2822. - # + # # It also provides methods you can call to determine if a string is safe module TextUtils @@ -118,19 +119,19 @@ module TMail ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{lwsp}]/n PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n - + # Returns true if the string supplied is free from characters not allowed as an ATOM def atom_safe?( str ) not ATOM_UNSAFE === str end - # If the string supplied has ATOM unsafe characters in it, will return the string quoted + # If the string supplied has ATOM unsafe characters in it, will return the string quoted # in double quotes, otherwise returns the string unmodified def quote_atom( str ) (ATOM_UNSAFE === str) ? dquote(str) : str end - # If the string supplied has PHRASE unsafe characters in it, will return the string quoted + # If the string supplied has PHRASE unsafe characters in it, will return the string quoted # in double quotes, otherwise returns the string unmodified def quote_phrase( str ) (PHRASE_UNSAFE === str) ? dquote(str) : str @@ -141,7 +142,7 @@ module TMail not TOKEN_UNSAFE === str end - # If the string supplied has TOKEN unsafe characters in it, will return the string quoted + # If the string supplied has TOKEN unsafe characters in it, will return the string quoted # in double quotes, otherwise returns the string unmodified def quote_token( str ) (TOKEN_UNSAFE === str) ? dquote(str) : str @@ -163,7 +164,7 @@ module TMail def unquote( str ) str =~ /^"(.*?)"$/m ? $1 : str end - + # Provides a method to join a domain name by it's parts and also makes it # ATOM safe by quoting it as needed def join_domain( arr ) @@ -256,7 +257,7 @@ module TMail MESSAGE_ID = /<[^\@>]+\@[^>]+>/ - + def message_id?( str ) MESSAGE_ID === str end @@ -267,7 +268,7 @@ module TMail def mime_encoded?( str ) MIME_ENCODED === str end - + def decode_params( hash ) new = Hash.new @@ -329,7 +330,7 @@ module TMail end end end - + # AppleMail generates illegal character contained Content-Type parameter like: # name==?ISO-2022-JP?B?...=?= # so quote. (This case is only value fits in one line.) @@ -338,11 +339,11 @@ module TMail head, should_quoted, tail = $~.captures # head: "; name=" # should_quoted: "=?ISO-2022-JP?B?...=?=" - + head << quote_token(should_quoted) << tail } end - + # AppleMail generates name=filename attributes in the content type that # contain spaces. Need to handle this so the TMail Parser can. def quote_unquoted_name @@ -353,9 +354,9 @@ module TMail head << quote_token(should_quoted) << tail } end - + #:startdoc: - + end end diff --git a/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/universaldetector.rb b/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/universaldetector.rb index 477db42..c8990c6 100644 --- a/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/universaldetector.rb +++ b/actionmailer/lib/action_mailer/vendor/tmail-1.2.7/tmail/vendor/rchardet-1.3/lib/rchardet/universaldetector.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii ######################## BEGIN LICENSE BLOCK ######################## # The Original Code is Mozilla Universal charset detector code. # @@ -15,12 +16,12 @@ # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. -# +# # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. -# +# # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA @@ -89,13 +90,13 @@ module CharDet @result = {'encoding' => "UTF-16BE", 'confidence' => 1.0} end end - + @_mGotData = true - if @result['encoding'] and (@result['confidence'] > 0.0) + if @result['encoding'] and (@result['confidence'] > 0.0) @done = true return end - + if @_mInputState == EPureAscii if @_highBitDetector =~ (aBuf) @_mInputState = EHighbyte @@ -103,7 +104,7 @@ module CharDet @_mInputState = EEscAscii end end - + @_mLastChar = aBuf[-1..-1] if @_mInputState == EEscAscii if not @_mEscCharSetProber @@ -128,9 +129,9 @@ module CharDet end end end - + end - + def close return if @done if not @_mGotData @@ -138,12 +139,12 @@ module CharDet return end @done = true - - if @_mInputState == EPureAscii + + if @_mInputState == EPureAscii @result = {'encoding' => 'ascii', 'confidence' => 1.0} return @result end - + if @_mInputState == EHighbyte confidences = {} @_mCharSetProbers.each{ |prober| confidences[prober] = prober.get_confidence } diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb index 298c7e4..1306541 100644 --- a/actionpack/test/controller/assert_select_test.rb +++ b/actionpack/test/controller/assert_select_test.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii #-- # Copyright (c) 2006 Assaf Arkin (http://labnotes.org) # Under MIT and/or CC By license. diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 81b015b..fc4804f 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii require 'abstract_unit' require 'controller/fake_controllers' require 'action_controller/routing/route_set' diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 9ef0564..d3c2726 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -1,3 +1,4 @@ +# encoding: us-ascii require 'abstract_unit' require 'testing_sandbox' begin -- 1.6.4.2 From 8716ee44e5ac203d4b9aa4eb4d41231a563503c2 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 24 Apr 2010 17:52:12 -0700 Subject: [PATCH 607/779] Fix unstated AS::Multibyte dependency --- actionpack/lib/action_view/helpers/tag_helper.rb | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index cdd348a..6a3bc59 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -1,3 +1,4 @@ +require 'active_support/multibyte' require 'active_support/core_ext/string/output_safety' require 'set' -- 1.6.4.2 From 9e262de3d872fa573b8a5743813de02f18b577e3 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 24 Apr 2010 19:38:10 -0700 Subject: [PATCH 608/779] Fix backport error: wrong exception name --- actionpack/test/template/render_test.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 4e668d1..454aa4c 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -301,7 +301,7 @@ class ReloadableRenderTest < Test::Unit::TestCase begin result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield") flunk 'Should have raised incompatible encoding error' - rescue ActionView::Template::Error => error + rescue ActionView::TemplateError => error assert_match 'invalid byte sequence in Shift_JIS', error.original_exception.message end end @@ -312,7 +312,7 @@ class ReloadableRenderTest < Test::Unit::TestCase begin result = @view.render(:file => "test/utf8_magic_with_bare_partial.html.erb", :layouts => "layouts/yield") flunk 'Should have raised incompatible encoding error' - rescue ActionView::Template::Error => error + rescue ActionView::TemplateError => error assert_match 'invalid byte sequence in Shift_JIS', error.original_exception.message end end -- 1.6.4.2 From aa401bd75a055a0bb12094128b01c8ce5fc2abe8 Mon Sep 17 00:00:00 2001 From: Joe Martinez Date: Mon, 26 Apr 2010 18:55:45 -0300 Subject: [PATCH 609/779] Add ActiveResource::Base.include_root_in_json, like Active Record, to serialize instances as hash of model name -> attributes hash rather than the bare attributes hash. [#2584 state:committed] Signed-off-by: Santiago Pastorino Signed-off-by: Jeremy Kemper --- actionpack/CHANGELOG | 2 ++ activeresource/lib/active_resource/base.rb | 9 +++++++++ activeresource/test/base_test.rb | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 0 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 1e02b04..ed1e24d 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *2.3.6 (unreleased)* +* JSON: set Base.include_root_in_json = true to include a root value in the JSON: {"post": {"title": ...}}. Mirrors the Active Record option. #2584 [Matthew Moore, Joe Martinez, Elad Meidar, Santiago Pastorino] + * Ruby 1.9: ERB template encoding using a magic comment at the top of the file. [Jeremy Kemper] <%# encoding: utf-8 %> diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index d904f40..94b2322 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -227,6 +227,9 @@ module ActiveResource # The logger for diagnosing and tracing Active Resource calls. cattr_accessor :logger + # Controls the top-level behavior of JSON serialization + cattr_accessor :include_root_in_json, :instance_writer => false + class << self # Gets the URI of the REST resources to map for this class. The site variable is required for # Active Resource's mapping to work. @@ -971,6 +974,12 @@ module ActiveResource case self.class.format when ActiveResource::Formats[:xml] self.class.format.encode(attributes, {:root => self.class.element_name}.merge(options)) + when ActiveResource::Formats::JsonFormat + if ActiveResource::Base.include_root_in_json + self.class.format.encode({self.class.element_name => attributes}, options) + else + self.class.format.encode(attributes, options) + end else self.class.format.encode(attributes, options) end diff --git a/activeresource/test/base_test.rb b/activeresource/test/base_test.rb index 527e099..1d1e84a 100644 --- a/activeresource/test/base_test.rb +++ b/activeresource/test/base_test.rb @@ -4,6 +4,7 @@ require "fixtures/customer" require "fixtures/street_address" require "fixtures/beast" require "fixtures/proxy" +require 'active_support/json' class BaseTest < Test::Unit::TestCase def setup @@ -13,6 +14,7 @@ class BaseTest < Test::Unit::TestCase @addy = { :id => 1, :street => '12345 Street' }.to_xml(:root => 'address') @default_request_headers = { 'Content-Type' => 'application/xml' } @rick = { :name => "Rick", :age => 25 }.to_xml(:root => "person") + @joe = {'person' => { :id => 6, :name => 'Joe' }}.to_json @people = [{ :id => 1, :name => 'Matz' }, { :id => 2, :name => 'David' }].to_xml(:root => 'people') @people_david = [{ :id => 2, :name => 'David' }].to_xml(:root => 'people') @addresses = [{ :id => 1, :street => '12345 Street' }].to_xml(:root => 'addresses') @@ -64,6 +66,7 @@ class BaseTest < Test::Unit::TestCase ActiveResource::HttpMock.respond_to do |mock| mock.get "/people/1.xml", {}, @matz mock.get "/people/2.xml", {}, @david + mock.get "/people/6.json", {}, @joe mock.get "/people/5.xml", {}, @marty mock.get "/people/Greg.xml", {}, @greg mock.get "/people/4.xml", {'key' => 'value'}, nil, 404 @@ -1005,6 +1008,18 @@ class BaseTest < Test::Unit::TestCase assert xml.include?('1') end + def test_to_json_including_root + Person.include_root_in_json = true + Person.format = :json + joe = Person.find(6) + json = joe.encode + assert_match '{"person":{"person":{', json + assert_match '"name":"Joe"', json + assert_match '"id":6', json + ensure + Person.format = :xml + end + def test_to_param_quacks_like_active_record new_person = Person.new assert_nil new_person.to_param -- 1.6.4.2 From ce5af2fefe0e447669fa8b7031f07558bfc84f4a Mon Sep 17 00:00:00 2001 From: Curtis Hawthorne Date: Sun, 21 Feb 2010 22:47:30 -0500 Subject: [PATCH 610/779] Destroy respects optimistic locking. Now works with :dependent => :destroy and includes unit tests for that case. Also includes better error messages when updating/deleting stale objects. [#1966 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/CHANGELOG | 2 + activerecord/lib/active_record/associations.rb | 11 ++++- .../lib/active_record/locking/optimistic.rb | 35 +++++++++++++- activerecord/test/cases/locking_test.rb | 51 ++++++++++++++++++- 4 files changed, 94 insertions(+), 5 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index e99265e..2ca6f07 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *2.3.6 (pending)* +* Destroy uses optimistic locking. If lock_version on the record you're destroying doesn't match lock_version in the database, a StaleObjectError is raised. #1966 [Curtis Hawthorne] + * To prefix the table names of all models in a module, define self.table_name_prefix on the module. #4032 [Andrew White] * Association inverses for belongs_to, has_one, and has_many. Optimization to reduce database queries. #3533 [Murray Steele] diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 53d28c0..fcc79b5 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1419,7 +1419,16 @@ module ActiveRecord when :destroy method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym define_method(method_name) do - send(reflection.name).each { |o| o.destroy } + send(reflection.name).each do |o| + # No point in executing the counter update since we're going to destroy the parent anyway + counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym + if(o.respond_to? counter_method) then + class << o + self + end.send(:define_method, counter_method, Proc.new {}) + end + o.destroy + end end before_destroy method_name when :delete_all diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index ff9899d..373f75e 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -23,6 +23,16 @@ module ActiveRecord # p2.first_name = "should fail" # p2.save # Raises a ActiveRecord::StaleObjectError # + # Optimistic locking will also check for stale data when objects are destroyed. Example: + # + # p1 = Person.find(1) + # p2 = Person.find(1) + # + # p1.first_name = "Michael" + # p1.save + # + # p2.destroy # Raises a ActiveRecord::StaleObjectError + # # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging, # or otherwise apply the business logic needed to resolve the conflict. # @@ -39,6 +49,7 @@ module ActiveRecord base.lock_optimistically = true base.alias_method_chain :update, :lock + base.alias_method_chain :destroy, :lock base.alias_method_chain :attributes_from_column_definition, :lock class << base @@ -86,7 +97,7 @@ module ActiveRecord end_sql unless affected_rows == 1 - raise ActiveRecord::StaleObjectError, "Attempted to update a stale object" + raise ActiveRecord::StaleObjectError, "Attempted to update a stale object: #{self.class.name}" end affected_rows @@ -98,6 +109,28 @@ module ActiveRecord end end + def destroy_with_lock #:nodoc: + return destroy_without_lock unless locking_enabled? + + unless new_record? + lock_col = self.class.locking_column + previous_value = send(lock_col).to_i + + affected_rows = connection.delete( + "DELETE FROM #{self.class.quoted_table_name} " + + "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id} " + + "AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}", + "#{self.class.name} Destroy" + ) + + unless affected_rows == 1 + raise ActiveRecord::StaleObjectError, "Attempted to delete a stale object: #{self.class.name}" + end + end + + freeze + end + module ClassMethods DEFAULT_LOCKING_COLUMN = 'lock_version' diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb index 8d2059d..cdd6591 100644 --- a/activerecord/test/cases/locking_test.rb +++ b/activerecord/test/cases/locking_test.rb @@ -38,6 +38,25 @@ class OptimisticLockingTest < ActiveRecord::TestCase assert_raise(ActiveRecord::StaleObjectError) { p2.save! } end + # See Lighthouse ticket #1966 + def test_lock_destroy + p1 = Person.find(1) + p2 = Person.find(1) + assert_equal 0, p1.lock_version + assert_equal 0, p2.lock_version + + p1.first_name = 'stu' + p1.save! + assert_equal 1, p1.lock_version + assert_equal 0, p2.lock_version + + assert_raises(ActiveRecord::StaleObjectError) { p2.destroy } + + assert p1.destroy + assert_equal true, p1.frozen? + assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) } + end + def test_lock_repeating p1 = Person.find(1) p2 = Person.find(1) @@ -150,6 +169,32 @@ class OptimisticLockingTest < ActiveRecord::TestCase end end end + + # See Lighthouse ticket #1966 + def test_destroy_dependents + # Establish dependent relationship between People and LegacyThing + add_counter_column_to(Person, 'legacy_things_count') + LegacyThing.connection.add_column LegacyThing.table_name, 'person_id', :integer + LegacyThing.reset_column_information + LegacyThing.class_eval do + belongs_to :person, :counter_cache => true + end + Person.class_eval do + has_many :legacy_things, :dependent => :destroy + end + + # Make sure that counter incrementing doesn't cause problems + p1 = Person.new(:first_name => 'fjord') + p1.save! + t = LegacyThing.new(:person => p1) + t.save! + p1.reload + assert_equal 1, p1.legacy_things_count + assert p1.destroy + assert_equal true, p1.frozen? + assert_raises(ActiveRecord::RecordNotFound) { Person.find(p1.id) } + assert_raises(ActiveRecord::RecordNotFound) { LegacyThing.find(t.id) } + end def test_quote_table_name ref = references(:michael_magician) @@ -168,11 +213,11 @@ class OptimisticLockingTest < ActiveRecord::TestCase private - def add_counter_column_to(model) - model.connection.add_column model.table_name, :test_count, :integer, :null => false, :default => 0 + def add_counter_column_to(model, col='test_count') + model.connection.add_column model.table_name, col, :integer, :null => false, :default => 0 model.reset_column_information # OpenBase does not set a value to existing rows when adding a not null default column - model.update_all(:test_count => 0) if current_adapter?(:OpenBaseAdapter) + model.update_all(col => 0) if current_adapter?(:OpenBaseAdapter) end def remove_counter_column_from(model) -- 1.6.4.2 From dcf0f97514851face2dd24b1f39eabee413fcfc2 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 20 Apr 2010 11:14:00 -0400 Subject: [PATCH 611/779] making rake:migrate VERSION=0 a noop called in succession. [#2137 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/migration.rb | 1 + activerecord/test/cases/migration_test.rb | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 0 deletions(-) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index c0b4b13..9f90e98 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -381,6 +381,7 @@ module ActiveRecord def migrate(migrations_path, target_version = nil) case when target_version.nil? then up(migrations_path, target_version) + when current_version == 0 && target_version == 0 then # noop when current_version > target_version then down(migrations_path, target_version) else up(migrations_path, target_version) end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 3d71e50..3552793 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1077,6 +1077,25 @@ if ActiveRecord::Base.connection.supports_migrations? load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb") end + def test_target_version_zero_should_run_only_once + # migrate up to 1 + ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1) + + # migrate down to 0 + ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0) + + # now unload the migrations that have been defined + PeopleHaveLastNames.unloadable + ActiveSupport::Dependencies.remove_unloadable_constants! + + # migrate down to 0 again + ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0) + + assert !defined? PeopleHaveLastNames + ensure + load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb") + end + def test_migrator_interleaved_migrations ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1") -- 1.6.4.2 From 0d767fd24f9fd2a4824e7620bc645d1cdd70e983 Mon Sep 17 00:00:00 2001 From: Marius Nuennerich Date: Sat, 1 May 2010 14:40:02 +0200 Subject: [PATCH 612/779] repair the activesupport message encryptor tests for me, do so in the same way as jeremy did with message verifier [#4517 state:committed] Signed-off-by: Jeremy Kemper --- activesupport/test/message_encryptor_test.rb | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index ed34615..1e16c9e 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -1,5 +1,12 @@ require 'abstract_unit' +begin + require 'openssl' + OpenSSL::Digest::SHA1 +rescue LoadError, NameError + $stderr.puts "Skipping MessageEncryptor test: broken OpenSSL install" +else + class MessageEncryptorTest < Test::Unit::TestCase def setup @encryptor = ActiveSupport::MessageEncryptor.new(ActiveSupport::SecureRandom.hex(64)) @@ -44,3 +51,5 @@ class MessageEncryptorTest < Test::Unit::TestCase ActiveSupport::Base64.encode64s(bits) end end + +end -- 1.6.4.2 From 04fa5af6a65d1d0e03d3c57d03a2fc8abd11909a Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 4 May 2010 17:51:22 +0100 Subject: [PATCH 613/779] Use class_inheritable_accessor for connection_handler --- .../abstract/connection_specification.rb | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index bbc290f..ff1ab7f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -10,8 +10,8 @@ module ActiveRecord ## # :singleton-method: # The connection handler - cattr_accessor :connection_handler, :instance_writer => false - @@connection_handler = ConnectionAdapters::ConnectionHandler.new + class_inheritable_accessor :connection_handler, :instance_writer => false + self.connection_handler = ConnectionAdapters::ConnectionHandler.new # Returns the connection currently associated with the class. This can # also be used to "borrow" the connection to do database work that isn't @@ -54,7 +54,7 @@ module ActiveRecord raise AdapterNotSpecified unless defined? RAILS_ENV establish_connection(RAILS_ENV) when ConnectionSpecification - @@connection_handler.establish_connection(name, spec) + self.connection_handler.establish_connection(name, spec) when Symbol, String if configuration = configurations[spec.to_s] establish_connection(configuration) -- 1.6.4.2 From 6dbc75fd7625c0248d269cc3127910cb4c888ed9 Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Tue, 4 May 2010 12:20:00 -0700 Subject: [PATCH 614/779] Allow pre-casted values (other than nil) to pass through from calculations un-touched [#4514 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/calculations.rb | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index eb149e8..a44f090 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -294,12 +294,15 @@ module ActiveRecord end def type_cast_calculated_value(value, column, operation = nil) - operation = operation.to_s.downcase - case operation + if value.is_a?(String) || value.nil? + case operation.to_s.downcase when 'count' then value.to_i when 'sum' then type_cast_using_column(value || '0', column) - when 'avg' then value && (value.is_a?(Fixnum) ? value.to_f : value).to_d + when 'avg' then value && (value.is_a?(Fixnum) ? value.to_f : value).to_d else type_cast_using_column(value, column) + end + else + value end end -- 1.6.4.2 From aeff1719ab9c4a0d1a9abd7a609ed8fb9646a0d8 Mon Sep 17 00:00:00 2001 From: Jatinder Singh Date: Fri, 30 Apr 2010 01:28:36 -0700 Subject: [PATCH 615/779] AR JSON Serializer now supports custom root option. Signed-off-by: Jeremy Kemper --- activerecord/CHANGELOG | 2 ++ .../active_record/serializers/json_serializer.rb | 2 +- activerecord/test/cases/json_serialization_test.rb | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 2ca6f07..5bb86d1 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *2.3.6 (pending)* +* JSON supports a custom root option: to_json(:root => 'custom') #4515 [Jatinder Singh] + * Destroy uses optimistic locking. If lock_version on the record you're destroying doesn't match lock_version in the database, a StaleObjectError is raised. #1966 [Curtis Hawthorne] * To prefix the table names of all models in a module, define self.table_name_prefix on the module. #4032 [Andrew White] diff --git a/activerecord/lib/active_record/serializers/json_serializer.rb b/activerecord/lib/active_record/serializers/json_serializer.rb index a20dfbc..5d49381 100644 --- a/activerecord/lib/active_record/serializers/json_serializer.rb +++ b/activerecord/lib/active_record/serializers/json_serializer.rb @@ -79,7 +79,7 @@ module ActiveRecord #:nodoc: def as_json(options = nil) #:nodoc: hash = Serializer.new(self, options).serializable_record - hash = { self.class.model_name.element => hash } if include_root_in_json + hash = { options[:root] || self.class.model_name.element => hash } if include_root_in_json hash end diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb index 54bc8e2..7f13e49 100644 --- a/activerecord/test/cases/json_serialization_test.rb +++ b/activerecord/test/cases/json_serialization_test.rb @@ -43,6 +43,20 @@ class JsonSerializationTest < ActiveRecord::TestCase Contact.include_root_in_json = false end + def test_should_include_root_in_json + Contact.include_root_in_json = true + json = @contact.to_json(:root => 'json_contact') + + assert_match %r{^\{"json_contact":\{}, json + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"age":16}, json + assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_match %r{"awesome":true}, json + assert_match %r{"preferences":\{"shows":"anime"\}}, json + ensure + Contact.include_root_in_json = false + end + def test_should_encode_all_encodable_attributes json = @contact.to_json -- 1.6.4.2 From f194d65f36be29971601664c880cb9b23ee2a95b Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 4 May 2010 22:42:25 +0100 Subject: [PATCH 616/779] Use primary key in conditions, not 'id' [#4395 state:resolved] Signed-off-by: Pratik Naik Conflicts: activerecord/test/cases/nested_attributes_test.rb --- .../lib/active_record/nested_attributes.rb | 2 +- activerecord/test/cases/nested_attributes_test.rb | 25 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 9288239..65434fb 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -346,7 +346,7 @@ module ActiveRecord association.to_a else attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact - attribute_ids.present? ? association.all(:conditions => {:id => attribute_ids}) : [] + attribute_ids.present? ? association.all(:conditions => {association.primary_key => attribute_ids}) : [] end attributes_collection.each do |attributes| diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 9f8534c..c001db6 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -6,6 +6,8 @@ require "models/parrot" require "models/treasure" require "models/man" require "models/interest" +require "models/owner" +require "models/pet" module AssertRaiseWithMessage def assert_raise_with_message(expected_exception, expected_message) @@ -706,3 +708,26 @@ class TestNestedAttributesLimit < ActiveRecord::TestCase end end end + +class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase + fixtures :owners, :pets + + def setup + Owner.accepts_nested_attributes_for :pets + + @owner = owners(:ashley) + @pet1, @pet2 = pets(:chew), pets(:mochi) + + @params = { + :pets_attributes => { + '0' => { :id => @pet1.id, :name => 'Foo' }, + '1' => { :id => @pet2.id, :name => 'Bar' } + } + } + end + + def test_should_update_existing_records_with_non_standard_primary_key + @owner.update_attributes(@params) + assert_equal ['Foo', 'Bar'], @owner.pets.map(&:name) + end +end -- 1.6.4.2 From 6d1344de5e6bcbd8de6ccd8cf2793fabe069102a Mon Sep 17 00:00:00 2001 From: Brian Lopez Date: Tue, 4 May 2010 13:46:52 -0700 Subject: [PATCH 617/779] add support for mysql2 adapter to dbconsole [#4532 state:committed] Signed-off-by: Jeremy Kemper --- railties/lib/commands/dbconsole.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/railties/lib/commands/dbconsole.rb b/railties/lib/commands/dbconsole.rb index e6f11a4..b416049 100644 --- a/railties/lib/commands/dbconsole.rb +++ b/railties/lib/commands/dbconsole.rb @@ -45,7 +45,7 @@ def find_cmd(*commands) end case config["adapter"] -when "mysql" +when /^mysql/ args = { 'host' => '--host', 'port' => '--port', -- 1.6.4.2 From adcfb4e8bd886fe9d14a9b97afdb9182d8abdc95 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 7 May 2010 15:32:16 -0300 Subject: [PATCH 618/779] simple_format should return html_safe but not escape text, that's for rails_xss plugin [#3767 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_view/helpers/text_helper.rb | 4 ++-- actionpack/test/template/text_helper_test.rb | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 9279a88..f2f1557 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -323,12 +323,12 @@ module ActionView # # => "

Look ma! A class!

" def simple_format(text, html_options={}) start_tag = tag('p', html_options, true) - text = h(text) + text = text.to_s.dup text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n text.gsub!(/\n\n+/, "

\n\n#{start_tag}") # 2+ newline -> paragraph text.gsub!(/([^\n]\n)(?=[^\n])/, '\1
') # 1 newline -> br text.insert 0, start_tag - text.safe_concat "

" + text.html_safe.safe_concat("

") end # Turns all URLs and e-mail addresses into clickable links. The :link option diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index d3c2726..6ee5ae1 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -44,10 +44,6 @@ class TextHelperTest < ActionView::TestCase assert simple_format(" test with html tags ").html_safe? end - def test_simple_format_should_escape_unsafe_input - assert_equal "

<b> test with unsafe string </b>

", simple_format(" test with unsafe string ") - end - def test_simple_format_should_not_escape_safe_input assert_equal "

test with safe string

", simple_format(" test with safe string ".html_safe) end -- 1.6.4.2 From f967b352d2abdb88b704266cdb06b53f7d8ab401 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 7 May 2010 16:58:24 -0300 Subject: [PATCH 619/779] =?UTF-8?q?Make=20find=5For=5Fcreate=20and=20find=5For=5Finitialize=20work=20mixing=20explicit=20parameters=20and=20a=20hash.=20ht:=20Marc-Andr=C3=A9=20Lafortune?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4457 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/CHANGELOG | 2 ++ activerecord/lib/active_record/base.rb | 23 ++++++++++++++--------- activerecord/test/cases/finder_test.rb | 18 +++++++++++++++++- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 5bb86d1..e9a1984 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *2.3.6 (pending)* +* find_or_create_by_attr(value, ...) works when attr is protected. #4457 [Santiago Pastorino, Marc-André Lafortune] + * JSON supports a custom root option: to_json(:root => 'custom') #4515 [Jatinder Singh] * Destroy uses optimistic locking. If lock_version on the record you're destroying doesn't match lock_version in the database, a StaleObjectError is raised. #1966 [Curtis Hawthorne] diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 32d2854..1067e36 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1942,23 +1942,28 @@ module ActiveRecord #:nodoc: # end self.class_eval <<-EOS, __FILE__, __LINE__ + 1 def self.#{method_id}(*args) - guard_protected_attributes = false - - if args[0].is_a?(Hash) - guard_protected_attributes = true - attributes = args[0].with_indifferent_access - find_attributes = attributes.slice(*[:#{attribute_names.join(',:')}]) - else - find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args) + attributes = [:#{attribute_names.join(',:')}] + protected_attributes_for_create = unprotected_attributes_for_create = {} + args.each_with_index do |arg, i| + if arg.is_a?(Hash) + protected_attributes_for_create = args[i].with_indifferent_access + else + unprotected_attributes_for_create[attributes[i]] = args[i] + end end + find_attributes = (protected_attributes_for_create.merge(unprotected_attributes_for_create)).slice(*attributes) + options = { :conditions => find_attributes } set_readonly_option!(options) record = find(:first, options) if record.nil? - record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) } + record = self.new do |r| + r.send(:attributes=, protected_attributes_for_create, true) unless protected_attributes_for_create.empty? + r.send(:attributes=, unprotected_attributes_for_create, false) unless unprotected_attributes_for_create.empty? + end #{'yield(record) if block_given?'} #{'record.save' if instantiator == :create} record diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 0959585..c779a69 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -838,7 +838,7 @@ class FinderTest < ActiveRecord::TestCase assert c.new_record? end - def test_find_or_create_from_one_attribute_should_set_not_attribute_even_when_protected + def test_find_or_create_from_one_attribute_should_not_set_attribute_even_when_protected c = Company.find_or_create_by_name({:name => "Fortune 1000", :rating => 1000}) assert_equal "Fortune 1000", c.name assert_not_equal 1000, c.rating @@ -846,6 +846,22 @@ class FinderTest < ActiveRecord::TestCase assert !c.new_record? end + def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected_and_also_set_the_hash + c = Company.find_or_initialize_by_rating(1000, {:name => "Fortune 1000"}) + assert_equal "Fortune 1000", c.name + assert_equal 1000, c.rating + assert c.valid? + assert c.new_record? + end + + def test_find_or_create_from_one_attribute_should_set_attribute_even_when_protected_and_also_set_the_hash + c = Company.find_or_create_by_rating(1000, {:name => "Fortune 1000"}) + assert_equal "Fortune 1000", c.name + assert_equal 1000, c.rating + assert c.valid? + assert !c.new_record? + end + def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected c = Company.find_or_initialize_by_name_and_rating("Fortune 1000", 1000) assert_equal "Fortune 1000", c.name -- 1.6.4.2 From 3616141fa2d2f35675d5962a1b329c8c51a5e9a3 Mon Sep 17 00:00:00 2001 From: Emili Parreno Date: Sat, 8 May 2010 12:35:39 +0100 Subject: [PATCH 620/779] Add index length support for MySQL [#1852 state:resolved] Example: add_index(:accounts, :name, :name => 'by_name', :length => 10) => CREATE INDEX by_name ON accounts(name(10)) add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15}) => CREATE INDEX by_name_surname ON accounts(name(10), surname(15)) Signed-off-by: Pratik Naik --- activerecord/CHANGELOG | 10 ++++++++ .../abstract/schema_definitions.rb | 2 +- .../abstract/schema_statements.rb | 23 +++++++++++++++++++- .../connection_adapters/mysql_adapter.rb | 17 +++++++++++++- activerecord/lib/active_record/schema_dumper.rb | 1 + .../test/cases/active_schema_test_mysql.rb | 17 ++++++++++++++ activerecord/test/cases/migration_test.rb | 8 +++++++ 7 files changed, 75 insertions(+), 3 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index e9a1984..8726874 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,15 @@ *2.3.6 (pending)* +* Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik] + + Example: + + add_index(:accounts, :name, :name => 'by_name', :length => 10) + => CREATE INDEX by_name ON accounts(name(10)) + + add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15}) + => CREATE INDEX by_name_surname ON accounts(name(10), surname(15)) + * find_or_create_by_attr(value, ...) works when attr is protected. #4457 [Santiago Pastorino, Marc-André Lafortune] * JSON supports a custom root option: to_json(:root => 'custom') #4515 [Jatinder Singh] diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 520f3c8..399a272 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -256,7 +256,7 @@ module ActiveRecord end end - class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc: + class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc: end # Abstract representation of a column definition. Instances of this type diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index d93cbc1..211cd7e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -246,18 +246,32 @@ module ActiveRecord # name. # # ===== Examples + # # ====== Creating a simple index # add_index(:suppliers, :name) # generates # CREATE INDEX suppliers_name_index ON suppliers(name) + # # ====== Creating a unique index # add_index(:accounts, [:branch_id, :party_id], :unique => true) # generates # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id) + # # ====== Creating a named index # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party') # generates # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id) + # + # ====== Creating an index with specific key length + # add_index(:accounts, :name, :name => 'by_name', :length => 10) + # generates + # CREATE INDEX by_name ON accounts(name(10)) + # + # add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15}) + # generates + # CREATE INDEX by_name_surname ON accounts(name(10), surname(15)) + # + # Note: SQLite doesn't support index length def add_index(table_name, column_name, options = {}) column_names = Array(column_name) index_name = index_name(table_name, :column => column_names) @@ -268,7 +282,9 @@ module ActiveRecord else index_type = options end - quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ") + + quoted_column_names = quoted_columns_for_index(column_names, options).join(", ") + execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})" end @@ -426,6 +442,11 @@ module ActiveRecord end protected + # Overridden by the mysql adapter for supporting index lengths + def quoted_columns_for_index(column_names, options = {}) + column_names.map {|name| quote_column_name(name) } + end + def options_include_default?(options) options.include?(:default) && !(options[:null] == false && options[:default].nil?) end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index a8aa16e..69e97b9 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -454,10 +454,11 @@ module ActiveRecord if current_index != row[2] next if row[2] == "PRIMARY" # skip the primary key current_index = row[2] - indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", []) + indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [], []) end indexes.last.columns << row[4] + indexes.last.lengths << row[7] end result.free indexes @@ -586,6 +587,20 @@ module ActiveRecord where_sql end + protected + def quoted_columns_for_index(column_names, options = {}) + length = options[:length] if options.is_a?(Hash) + + quoted_column_names = case length + when Hash + column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) } + when Fixnum + column_names.map {|name| "#{quote_column_name(name)}(#{length})"} + else + column_names.map {|name| quote_column_name(name) } + end + end + private def connect encoding = @config[:encoding] diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 68a45a0..5f1207f 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -170,6 +170,7 @@ HEADER statment_parts << index.columns.inspect statment_parts << (':name => ' + index.name.inspect) statment_parts << ':unique => true' if index.unique + statment_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index.lengths.compact.present? ' ' + statment_parts.join(', ') end diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb index 9aff538..f4d123b 100644 --- a/activerecord/test/cases/active_schema_test_mysql.rb +++ b/activerecord/test/cases/active_schema_test_mysql.rb @@ -15,6 +15,23 @@ class ActiveSchemaTest < ActiveRecord::TestCase end end + def test_add_index + expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" + assert_equal expected, add_index(:people, :last_name, :length => nil) + + expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10))" + assert_equal expected, add_index(:people, :last_name, :length => 10) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15))" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`)" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15}) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10}) + end + def test_drop_table assert_equal "DROP TABLE `people`", drop_table(:people) end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 3552793..ea939ae 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -92,6 +92,14 @@ if ActiveRecord::Base.connection.supports_migrations? assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") } assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) } + assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) } + assert_nothing_raised { Person.connection.remove_index("people", "last_name") } + assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => {:last_name => 10}) } + assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) } + assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => 10) } + assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) } + assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20}) } + assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) } end # quoting -- 1.6.4.2 From 555801c908851ea4d37f511397e049d21dcca9b5 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 8 May 2010 17:25:40 -0300 Subject: [PATCH 621/779] The intention here was to assign to different objects ht: Radar --- activerecord/lib/active_record/base.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 1067e36..0699c87 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1943,7 +1943,7 @@ module ActiveRecord #:nodoc: self.class_eval <<-EOS, __FILE__, __LINE__ + 1 def self.#{method_id}(*args) attributes = [:#{attribute_names.join(',:')}] - protected_attributes_for_create = unprotected_attributes_for_create = {} + protected_attributes_for_create, unprotected_attributes_for_create = {}, {} args.each_with_index do |arg, i| if arg.is_a?(Hash) protected_attributes_for_create = args[i].with_indifferent_access -- 1.6.4.2 From eababa35cf5917c4ebd3f48cb29b9a2f6d1db404 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Sat, 8 May 2010 23:56:06 +0300 Subject: [PATCH 622/779] Revert "Add index length support for MySQL [#1852 state:resolved]" (breaks the build) This reverts commit 3616141fa2d2f35675d5962a1b329c8c51a5e9a3. --- activerecord/CHANGELOG | 10 -------- .../abstract/schema_definitions.rb | 2 +- .../abstract/schema_statements.rb | 23 +------------------- .../connection_adapters/mysql_adapter.rb | 17 +------------- activerecord/lib/active_record/schema_dumper.rb | 1 - .../test/cases/active_schema_test_mysql.rb | 17 -------------- activerecord/test/cases/migration_test.rb | 8 ------- 7 files changed, 3 insertions(+), 75 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 8726874..e9a1984 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,15 +1,5 @@ *2.3.6 (pending)* -* Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik] - - Example: - - add_index(:accounts, :name, :name => 'by_name', :length => 10) - => CREATE INDEX by_name ON accounts(name(10)) - - add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15}) - => CREATE INDEX by_name_surname ON accounts(name(10), surname(15)) - * find_or_create_by_attr(value, ...) works when attr is protected. #4457 [Santiago Pastorino, Marc-André Lafortune] * JSON supports a custom root option: to_json(:root => 'custom') #4515 [Jatinder Singh] diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 399a272..520f3c8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -256,7 +256,7 @@ module ActiveRecord end end - class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc: + class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc: end # Abstract representation of a column definition. Instances of this type diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 211cd7e..d93cbc1 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -246,32 +246,18 @@ module ActiveRecord # name. # # ===== Examples - # # ====== Creating a simple index # add_index(:suppliers, :name) # generates # CREATE INDEX suppliers_name_index ON suppliers(name) - # # ====== Creating a unique index # add_index(:accounts, [:branch_id, :party_id], :unique => true) # generates # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id) - # # ====== Creating a named index # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party') # generates # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id) - # - # ====== Creating an index with specific key length - # add_index(:accounts, :name, :name => 'by_name', :length => 10) - # generates - # CREATE INDEX by_name ON accounts(name(10)) - # - # add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15}) - # generates - # CREATE INDEX by_name_surname ON accounts(name(10), surname(15)) - # - # Note: SQLite doesn't support index length def add_index(table_name, column_name, options = {}) column_names = Array(column_name) index_name = index_name(table_name, :column => column_names) @@ -282,9 +268,7 @@ module ActiveRecord else index_type = options end - - quoted_column_names = quoted_columns_for_index(column_names, options).join(", ") - + quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ") execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})" end @@ -442,11 +426,6 @@ module ActiveRecord end protected - # Overridden by the mysql adapter for supporting index lengths - def quoted_columns_for_index(column_names, options = {}) - column_names.map {|name| quote_column_name(name) } - end - def options_include_default?(options) options.include?(:default) && !(options[:null] == false && options[:default].nil?) end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 69e97b9..a8aa16e 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -454,11 +454,10 @@ module ActiveRecord if current_index != row[2] next if row[2] == "PRIMARY" # skip the primary key current_index = row[2] - indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [], []) + indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", []) end indexes.last.columns << row[4] - indexes.last.lengths << row[7] end result.free indexes @@ -587,20 +586,6 @@ module ActiveRecord where_sql end - protected - def quoted_columns_for_index(column_names, options = {}) - length = options[:length] if options.is_a?(Hash) - - quoted_column_names = case length - when Hash - column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) } - when Fixnum - column_names.map {|name| "#{quote_column_name(name)}(#{length})"} - else - column_names.map {|name| quote_column_name(name) } - end - end - private def connect encoding = @config[:encoding] diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 5f1207f..68a45a0 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -170,7 +170,6 @@ HEADER statment_parts << index.columns.inspect statment_parts << (':name => ' + index.name.inspect) statment_parts << ':unique => true' if index.unique - statment_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index.lengths.compact.present? ' ' + statment_parts.join(', ') end diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb index f4d123b..9aff538 100644 --- a/activerecord/test/cases/active_schema_test_mysql.rb +++ b/activerecord/test/cases/active_schema_test_mysql.rb @@ -15,23 +15,6 @@ class ActiveSchemaTest < ActiveRecord::TestCase end end - def test_add_index - expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" - assert_equal expected, add_index(:people, :last_name, :length => nil) - - expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10))" - assert_equal expected, add_index(:people, :last_name, :length => 10) - - expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15))" - assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15) - - expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`)" - assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15}) - - expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))" - assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10}) - end - def test_drop_table assert_equal "DROP TABLE `people`", drop_table(:people) end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index ea939ae..3552793 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -92,14 +92,6 @@ if ActiveRecord::Base.connection.supports_migrations? assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") } assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) } - assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) } - assert_nothing_raised { Person.connection.remove_index("people", "last_name") } - assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => {:last_name => 10}) } - assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) } - assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => 10) } - assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) } - assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20}) } - assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) } end # quoting -- 1.6.4.2 From 77adb4bc2019e3a5b64ae8678310d176e30834d0 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 9 May 2010 12:34:07 +0100 Subject: [PATCH 623/779] Revert "Revert "Add index length support for MySQL [#1852 state:resolved]" (breaks the build)" This reverts commit eababa35cf5917c4ebd3f48cb29b9a2f6d1db404. --- activerecord/CHANGELOG | 10 ++++++++ .../abstract/schema_definitions.rb | 2 +- .../abstract/schema_statements.rb | 23 +++++++++++++++++++- .../connection_adapters/mysql_adapter.rb | 17 +++++++++++++- activerecord/lib/active_record/schema_dumper.rb | 1 + .../test/cases/active_schema_test_mysql.rb | 17 ++++++++++++++ activerecord/test/cases/migration_test.rb | 8 +++++++ 7 files changed, 75 insertions(+), 3 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index e9a1984..8726874 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,15 @@ *2.3.6 (pending)* +* Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik] + + Example: + + add_index(:accounts, :name, :name => 'by_name', :length => 10) + => CREATE INDEX by_name ON accounts(name(10)) + + add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15}) + => CREATE INDEX by_name_surname ON accounts(name(10), surname(15)) + * find_or_create_by_attr(value, ...) works when attr is protected. #4457 [Santiago Pastorino, Marc-André Lafortune] * JSON supports a custom root option: to_json(:root => 'custom') #4515 [Jatinder Singh] diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 520f3c8..399a272 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -256,7 +256,7 @@ module ActiveRecord end end - class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc: + class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc: end # Abstract representation of a column definition. Instances of this type diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index d93cbc1..211cd7e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -246,18 +246,32 @@ module ActiveRecord # name. # # ===== Examples + # # ====== Creating a simple index # add_index(:suppliers, :name) # generates # CREATE INDEX suppliers_name_index ON suppliers(name) + # # ====== Creating a unique index # add_index(:accounts, [:branch_id, :party_id], :unique => true) # generates # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id) + # # ====== Creating a named index # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party') # generates # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id) + # + # ====== Creating an index with specific key length + # add_index(:accounts, :name, :name => 'by_name', :length => 10) + # generates + # CREATE INDEX by_name ON accounts(name(10)) + # + # add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15}) + # generates + # CREATE INDEX by_name_surname ON accounts(name(10), surname(15)) + # + # Note: SQLite doesn't support index length def add_index(table_name, column_name, options = {}) column_names = Array(column_name) index_name = index_name(table_name, :column => column_names) @@ -268,7 +282,9 @@ module ActiveRecord else index_type = options end - quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ") + + quoted_column_names = quoted_columns_for_index(column_names, options).join(", ") + execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})" end @@ -426,6 +442,11 @@ module ActiveRecord end protected + # Overridden by the mysql adapter for supporting index lengths + def quoted_columns_for_index(column_names, options = {}) + column_names.map {|name| quote_column_name(name) } + end + def options_include_default?(options) options.include?(:default) && !(options[:null] == false && options[:default].nil?) end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index a8aa16e..69e97b9 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -454,10 +454,11 @@ module ActiveRecord if current_index != row[2] next if row[2] == "PRIMARY" # skip the primary key current_index = row[2] - indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", []) + indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [], []) end indexes.last.columns << row[4] + indexes.last.lengths << row[7] end result.free indexes @@ -586,6 +587,20 @@ module ActiveRecord where_sql end + protected + def quoted_columns_for_index(column_names, options = {}) + length = options[:length] if options.is_a?(Hash) + + quoted_column_names = case length + when Hash + column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) } + when Fixnum + column_names.map {|name| "#{quote_column_name(name)}(#{length})"} + else + column_names.map {|name| quote_column_name(name) } + end + end + private def connect encoding = @config[:encoding] diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 68a45a0..5f1207f 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -170,6 +170,7 @@ HEADER statment_parts << index.columns.inspect statment_parts << (':name => ' + index.name.inspect) statment_parts << ':unique => true' if index.unique + statment_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index.lengths.compact.present? ' ' + statment_parts.join(', ') end diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb index 9aff538..f4d123b 100644 --- a/activerecord/test/cases/active_schema_test_mysql.rb +++ b/activerecord/test/cases/active_schema_test_mysql.rb @@ -15,6 +15,23 @@ class ActiveSchemaTest < ActiveRecord::TestCase end end + def test_add_index + expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" + assert_equal expected, add_index(:people, :last_name, :length => nil) + + expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10))" + assert_equal expected, add_index(:people, :last_name, :length => 10) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15))" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`)" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15}) + + expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))" + assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10}) + end + def test_drop_table assert_equal "DROP TABLE `people`", drop_table(:people) end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 3552793..ea939ae 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -92,6 +92,14 @@ if ActiveRecord::Base.connection.supports_migrations? assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") } assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) } assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) } + assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) } + assert_nothing_raised { Person.connection.remove_index("people", "last_name") } + assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => {:last_name => 10}) } + assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) } + assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => 10) } + assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) } + assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20}) } + assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) } end # quoting -- 1.6.4.2 From 3c1bb40b6b0b611ba068c2e5375cbcc400c40507 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 9 May 2010 12:42:15 +0100 Subject: [PATCH 624/779] Make sure schema dumper doesnt throw up when there are no index lengths --- activerecord/lib/active_record/schema_dumper.rb | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 5f1207f..1a21f86 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -170,7 +170,9 @@ HEADER statment_parts << index.columns.inspect statment_parts << (':name => ' + index.name.inspect) statment_parts << ':unique => true' if index.unique - statment_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index.lengths.compact.present? + + index_lengths = index.lengths.compact if index.lengths.is_a?(Array) + statment_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present? ' ' + statment_parts.join(', ') end -- 1.6.4.2 From 8e679f1854f05c9352b47bd688d8604d981eae06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 11 May 2010 22:11:44 +0200 Subject: [PATCH 625/779] Remove i18n interpolation deprecation. It has been around since 2.2. --- .../i18n_interpolation_deprecation.rb | 26 -------------------- 1 files changed, 0 insertions(+), 26 deletions(-) delete mode 100644 activerecord/lib/active_record/i18n_interpolation_deprecation.rb diff --git a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb b/activerecord/lib/active_record/i18n_interpolation_deprecation.rb deleted file mode 100644 index ccb0cac..0000000 --- a/activerecord/lib/active_record/i18n_interpolation_deprecation.rb +++ /dev/null @@ -1,26 +0,0 @@ -# Deprecates the use of the former message interpolation syntax in activerecord -# as in "must have %d characters". The new syntax uses explicit variable names -# as in "{{value}} must have {{count}} characters". - -require 'i18n/backend/simple' -module I18n - module Backend - class Simple - DEPRECATED_INTERPOLATORS = { '%d' => '{{count}}', '%s' => '{{value}}' } - - protected - def interpolate_with_deprecated_syntax(locale, string, values = {}) - return string unless string.is_a?(String) && !values.empty? - - string = string.gsub(/%d|%s/) do |s| - instead = DEPRECATED_INTERPOLATORS[s] - ActiveSupport::Deprecation.warn "using #{s} in messages is deprecated; use #{instead} instead." - instead - end - - interpolate_without_deprecated_syntax(locale, string, values) - end - alias_method_chain :interpolate, :deprecated_syntax - end - end -end \ No newline at end of file -- 1.6.4.2 From ad73a3aec4665d8a44060640e51075fd732c33a1 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 12 May 2010 02:00:04 -0300 Subject: [PATCH 626/779] type_cast_calculated_value refactor: value is never a Fixnum here. Fix test since SQLite returns Float. [#4514 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/calculations.rb | 2 +- activerecord/test/cases/calculations_test.rb | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index a44f090..0df2b6b 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -298,7 +298,7 @@ module ActiveRecord case operation.to_s.downcase when 'count' then value.to_i when 'sum' then type_cast_using_column(value || '0', column) - when 'avg' then value && (value.is_a?(Fixnum) ? value.to_f : value).to_d + when 'avg' then value.try(:to_d) else type_cast_using_column(value, column) end else diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index f428052..60f41a8 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -23,8 +23,7 @@ class CalculationsTest < ActiveRecord::TestCase def test_should_average_field value = Account.average(:credit_limit) - assert_kind_of BigDecimal, value - assert_equal BigDecimal.new('53.0'), value + assert_equal 53.0, value end def test_should_return_nil_as_average -- 1.6.4.2 From 9ab11545235f939a58a81c0aa90c6782424f4410 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 11 May 2010 22:39:53 -0700 Subject: [PATCH 627/779] Drop require removed by 8e679f1 --- activerecord/lib/active_record.rb | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 4409304..0d0ada9 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -80,5 +80,4 @@ module ActiveRecord end end -require 'active_record/i18n_interpolation_deprecation' I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml' -- 1.6.4.2 From 7cbc546d39e7ba4e51d1f3cec80f454fdcd303ef Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 12 May 2010 17:43:02 +0100 Subject: [PATCH 628/779] Use superclass_delegating_accessor for connection handlers --- .../abstract/connection_specification.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index ff1ab7f..947367f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -10,7 +10,7 @@ module ActiveRecord ## # :singleton-method: # The connection handler - class_inheritable_accessor :connection_handler, :instance_writer => false + superclass_delegating_accessor :connection_handler, :instance_writer => false self.connection_handler = ConnectionAdapters::ConnectionHandler.new # Returns the connection currently associated with the class. This can -- 1.6.4.2 From c4ef7bb2a0a2e2351f5ed2b1f98c9b40c441591e Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Wed, 12 May 2010 17:31:26 -0300 Subject: [PATCH 629/779] to_json and to_xml tests added to ActiveResource [#4529 state:resolved] Signed-off-by: Jeremy Kemper --- activeresource/test/base_test.rb | 34 ++++++++++++++++++++++++++++++++++ 1 files changed, 34 insertions(+), 0 deletions(-) diff --git a/activeresource/test/base_test.rb b/activeresource/test/base_test.rb index 1d1e84a..2a9ca65 100644 --- a/activeresource/test/base_test.rb +++ b/activeresource/test/base_test.rb @@ -1008,6 +1008,21 @@ class BaseTest < Test::Unit::TestCase assert xml.include?('1') end + def test_to_xml_with_element_name + old_elem_name = Person.element_name + matz = Person.find(1) + Person.element_name = 'ruby_creator' + xml = matz.encode + + assert xml.include?('') + assert xml.include?('') + assert xml.include?('Matz') + assert xml.include?('1') + assert xml.include?('') + ensure + Person.element_name = old_elem_name + end + def test_to_json_including_root Person.include_root_in_json = true Person.format = :json @@ -1018,6 +1033,25 @@ class BaseTest < Test::Unit::TestCase assert_match '"id":6', json ensure Person.format = :xml + Person.include_root_in_json = false + end + + def test_to_json_with_element_name + old_elem_name = Person.element_name + Person.include_root_in_json = true + Person.format = :json + joe = Person.find(6) + Person.element_name = 'ruby_creator' + json = joe.encode + Person.format = :xml + + assert_match %r{^\{"ruby_creator":\{"person":\{}, json + assert_match %r{"id":6}, json + assert_match %r{"name":"Joe"}, json + assert_match %r{\}\}\}$}, json + ensure + Person.element_name = old_elem_name + Person.include_root_in_json = false end def test_to_param_quacks_like_active_record -- 1.6.4.2 From 6949d6f54f135da6331f8e31629e02961cf71b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 14 May 2010 23:43:21 +0200 Subject: [PATCH 630/779] Remove deprecated tests according to 8e679f1 --- activerecord/test/cases/validations_i18n_test.rb | 36 ---------------------- 1 files changed, 0 insertions(+), 36 deletions(-) diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 9d29385..314a893 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -27,42 +27,6 @@ module ActiveRecordValidationsI18nTestHelper end end -# DEPRECATIONS - -class ActiveRecordValidationsI18nDeprecationsTests < ActiveSupport::TestCase - test "default_error_messages is deprecated and can be removed in Rails 3 / ActiveModel" do - assert_deprecated('ActiveRecord::Errors.default_error_messages') do - ActiveRecord::Errors.default_error_messages - end - end - - test "%s interpolation syntax in error messages still works" do - ActiveSupport::Deprecation.silence do - result = I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this' - assert_equal result, "this interpolation syntax is deprecated" - end - end - - test "%s interpolation syntax in error messages is deprecated" do - assert_deprecated('using %s in messages') do - I18n.t :does_not_exist, :default => "%s interpolation syntax is deprected", :value => 'this' - end - end - - test "%d interpolation syntax in error messages still works" do - ActiveSupport::Deprecation.silence do - result = I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprecated", :count => 2 - assert_equal result, "2 interpolation syntaxes are deprecated" - end - end - - test "%d interpolation syntax in error messages is deprecated" do - assert_deprecated('using %d in messages') do - I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected", :count => 2 - end - end -end - # ACTIVERECORD VALIDATIONS # -- 1.6.4.2 From 8e7a64d09003720b333932f86f21eafdf99193bd Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Tue, 11 May 2010 22:01:37 +0200 Subject: [PATCH 631/779] backported AR correction to find_each and find_in_batches to raise when the user uses select but does not specify the primary key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/batches.rb | 7 ++++++- activerecord/test/cases/batches_test.rb | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/batches.rb b/activerecord/lib/active_record/batches.rb index 99c4598..acbbd98 100644 --- a/activerecord/lib/active_record/batches.rb +++ b/activerecord/lib/active_record/batches.rb @@ -66,7 +66,12 @@ module ActiveRecord yield records break if records.size < batch_size - records = proxy.find(:all, :conditions => [ "#{table_name}.#{primary_key} > ?", records.last.id ]) + + last_value = records.last.id + + raise "You must include the primary key if you define a select" unless last_value.present? + + records = proxy.find(:all, :conditions => [ "#{table_name}.#{primary_key} > ?", last_value ]) end end diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index 68b9ffd..3bf8bc8 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -64,4 +64,18 @@ class EachTest < ActiveRecord::TestCase assert_nothing_raised { Post.find(posts(:thinking).id) } end end + + def test_each_should_raise_if_select_is_set_without_id + assert_raise(RuntimeError) do + Post.find_each(:select => :title, :batch_size => 1) { |post| post } + end + end + + def test_each_should_execute_if_id_is_in_select + assert_queries(4) do + Post.find_each(:select => "id, title, type", :batch_size => 2) do |post| + assert_kind_of Post, post + end + end + end end \ No newline at end of file -- 1.6.4.2 From 2de364636c06487faf48cbf9198bccd90a02eaf8 Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Sat, 15 May 2010 14:06:39 +0200 Subject: [PATCH 632/779] partial counters with :as [#2804 state:resolved] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_view/partials.rb | 1 + actionpack/test/controller/render_test.rb | 9 +++++++++ .../fixtures/test/_customer_counter_with_as.erb | 1 + 3 files changed, 11 insertions(+), 0 deletions(-) create mode 100644 actionpack/test/fixtures/test/_customer_counter_with_as.erb diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index 2dfd267..02c4e9f 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -218,6 +218,7 @@ module ActionView ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path) template = _pick_partial_template(_partial_path) local_assigns[template.counter_name] = index + local_assigns["#{as.to_s}_counter".to_sym] = local_assigns[template.counter_name] result = template.render_partial(self, object, local_assigns.dup, as) index += 1 result diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 331683e..42f776c 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -652,6 +652,10 @@ class TestController < ActionController::Base render :partial => "customer_counter", :collection => [ Customer.new("david"), Customer.new("mary") ] end + def partial_collection_with_as_and_counter + render :partial => "customer_counter_with_as", :collection => [ Customer.new("david"), Customer.new("mary") ], :as => :client + end + def partial_collection_with_locals render :partial => "customer_greeting", :collection => [ Customer.new("david"), Customer.new("mary") ], :locals => { :greeting => "Bonjour" } end @@ -1470,6 +1474,11 @@ class RenderTest < ActionController::TestCase assert_equal "david0mary1", @response.body end + def test_partial_collection_with_as_and_counter + get :partial_collection_with_as_and_counter + assert_equal "david0mary1", @response.body + end + def test_partial_collection_with_locals get :partial_collection_with_locals assert_equal "Bonjour: davidBonjour: mary", @response.body diff --git a/actionpack/test/fixtures/test/_customer_counter_with_as.erb b/actionpack/test/fixtures/test/_customer_counter_with_as.erb new file mode 100644 index 0000000..1241eb6 --- /dev/null +++ b/actionpack/test/fixtures/test/_customer_counter_with_as.erb @@ -0,0 +1 @@ +<%= client.name %><%= client_counter %> \ No newline at end of file -- 1.6.4.2 From 22b020db3ed3992b510b91d3fd423e510b9369e4 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 20 Apr 2010 13:50:26 -0400 Subject: [PATCH 633/779] db:drop:all throws error when database does not exist [#2997 state:resolved] Signed-off-by: Jeremy Kemper --- railties/lib/tasks/databases.rake | 28 ++++++++++++++-------------- 1 files changed, 14 insertions(+), 14 deletions(-) diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake index 8b60839..0c83311 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/tasks/databases.rake @@ -94,11 +94,7 @@ namespace :db do desc 'Drops the database for the current RAILS_ENV' task :drop => :load_config do config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] - begin - drop_database(config) - rescue Exception => e - puts "Couldn't drop #{config['database']} : #{e.inspect}" - end + drop_database(config) end def local_database?(config, &block) @@ -410,15 +406,19 @@ namespace :db do end def drop_database(config) - case config['adapter'] - when 'mysql' - ActiveRecord::Base.establish_connection(config) - ActiveRecord::Base.connection.drop_database config['database'] - when /^sqlite/ - FileUtils.rm(File.join(RAILS_ROOT, config['database'])) - when 'postgresql' - ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) - ActiveRecord::Base.connection.drop_database config['database'] + begin + case config['adapter'] + when 'mysql' + ActiveRecord::Base.establish_connection(config) + ActiveRecord::Base.connection.drop_database config['database'] + when /^sqlite/ + FileUtils.rm(File.join(RAILS_ROOT, config['database'])) + when 'postgresql' + ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) + ActiveRecord::Base.connection.drop_database config['database'] + end + rescue Exception => e + puts "Couldn't drop #{config['database']} : #{e.inspect}" end end -- 1.6.4.2 From 5d7ad7ba41c1c4298ad6467d63ee20e6c6acdbfd Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Sat, 15 May 2010 20:07:13 +0200 Subject: [PATCH 634/779] make sure `as` is set before trying to build an #{as}_counter. [#2804 state:resolved] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_view/partials.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index 02c4e9f..3aeebc6 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -218,7 +218,7 @@ module ActionView ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path) template = _pick_partial_template(_partial_path) local_assigns[template.counter_name] = index - local_assigns["#{as.to_s}_counter".to_sym] = local_assigns[template.counter_name] + local_assigns["#{as.to_s}_counter".to_sym] = local_assigns[template.counter_name] if as result = template.render_partial(self, object, local_assigns.dup, as) index += 1 result -- 1.6.4.2 From 0706bdce603897a869b9af753fddb81a9c9dc7ec Mon Sep 17 00:00:00 2001 From: Lawrence Pit Date: Fri, 11 Sep 2009 16:02:00 +1000 Subject: [PATCH 635/779] docs + test for each_error [#3185 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/validations.rb | 12 ++++++++++++ activerecord/test/cases/validations_test.rb | 13 ++++++++++++- 2 files changed, 24 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 41d28f3..17cbcb9 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -239,6 +239,18 @@ module ActiveRecord @errors.each_key { |attr| @errors[attr].each { |error| yield attr, error.message } } end + # Yields each attribute and associated error per error added. + # + # class Company < ActiveRecord::Base + # validates_presence_of :name, :address, :email + # validates_length_of :name, :in => 5..30 + # end + # + # company = Company.create(:address => '123 First St.') + # company.errors.each_error{|attr,err| puts "#{attr} - #{err.type}" } + # # => name - :too_short + # # name - :blank + # # address - :blank def each_error @errors.each_key { |attr| @errors[attr].each { |error| yield attr, error } } end diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 72dc622..c33e4ea 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -188,7 +188,7 @@ class ValidationsTest < ActiveRecord::TestCase end end - def test_single_error_per_attr_iteration + def test_single_error_string_per_attr_iteration r = Reply.new r.save @@ -199,6 +199,17 @@ class ValidationsTest < ActiveRecord::TestCase assert errors.include?(["content", "Empty"]) end + def test_single_error_object_per_attr_iteration + r = Reply.new + r.save + + errors = [] + r.errors.each_error { |attr, error| errors << [attr, error.attribute] } + + assert errors.include?(["title", "title"]) + assert errors.include?(["content", "content"]) + end + def test_multiple_errors_per_attr_iteration_with_full_error_composition r = Reply.new r.title = "Wrong Create" -- 1.6.4.2 From ddadcc7cf8332415ba48b66904ee6136b43dda6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elomar=20Fran=C3=A7a?= Date: Sun, 27 Sep 2009 13:42:13 -0300 Subject: [PATCH 636/779] Don't carry default value when changing column for a binary type on MySQL [#3234 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../connection_adapters/mysql_adapter.rb | 6 +++++- activerecord/test/cases/migration_test.rb | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 69e97b9..2c8f08a 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -506,7 +506,7 @@ module ActiveRecord def change_column(table_name, column_name, type, options = {}) #:nodoc: column = column_for(table_name, column_name) - unless options_include_default?(options) + if has_default?(type) && !options_include_default?(options) options[:default] = column.default end @@ -655,6 +655,10 @@ module ActiveRecord end column end + + def has_default?(sql_type) + sql_type =~ :binary || sql_type == :text #mysql forbids defaults on blob and text columns + end end end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index ea939ae..ffb9e21 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -823,6 +823,16 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal "Tester", Person.new.first_name end + def test_change_column_type_default_should_change + old_columns = Person.connection.columns(Person.table_name, "#{name} Columns") + assert !old_columns.find { |c| c.name == 'data' } + + assert_nothing_raised do + Person.connection.add_column "people", "data", :string, :default => '' + Person.connection.change_column "people", "data", :binary + end + end + def test_change_column_quotes_column_names Person.connection.create_table :testings do |t| t.column :select, :string -- 1.6.4.2 From 3568c5cee0f37f3302d739472be7477bee1b0d2a Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Fri, 14 May 2010 22:28:10 +0200 Subject: [PATCH 637/779] using :time_select when the attribute type is :time in the scaffold generator. [#2377 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/rails_generator/generated_attribute.rb | 13 +++++++------ 1 files changed, 7 insertions(+), 6 deletions(-) diff --git a/railties/lib/rails_generator/generated_attribute.rb b/railties/lib/rails_generator/generated_attribute.rb index a3d4a01..81c3bfd 100644 --- a/railties/lib/rails_generator/generated_attribute.rb +++ b/railties/lib/rails_generator/generated_attribute.rb @@ -12,12 +12,13 @@ module Rails def field_type @field_type ||= case type - when :integer, :float, :decimal then :text_field - when :datetime, :timestamp, :time then :datetime_select - when :date then :date_select - when :string then :text_field - when :text then :text_area - when :boolean then :check_box + when :integer, :float, :decimal then :text_field + when :time then :time_select + when :datetime, :timestamp then :datetime_select + when :date then :date_select + when :string then :text_field + when :text then :text_area + when :boolean then :check_box else :text_field end -- 1.6.4.2 From 3570f3e7f692e1d43e6be4d65bc6de986748cab8 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Mon, 10 May 2010 06:51:53 -0400 Subject: [PATCH 638/779] Fix broken integration test in 2.x [#4565 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_controller/integration.rb | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 11f4d99..241b2c2 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -355,6 +355,8 @@ module ActionController # used in integration tests. @response.extend(TestResponseBehavior) + body.close if body.respond_to?(:close) + return @status rescue MultiPartNeededException boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1" -- 1.6.4.2 From e535b45c8661a29a04f46e1b8cc9af9fc59c6393 Mon Sep 17 00:00:00 2001 From: Nobuhiro IMAI Date: Sat, 30 May 2009 14:58:47 +0900 Subject: [PATCH 639/779] make fixture accessors private MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit prevent to be run fixture accessor (e.g. test_foos for TestFoo model) as a test case Signed-off-by: José Valim --- activerecord/lib/active_record/fixtures.rb | 1 + activerecord/test/cases/fixtures_test.rb | 5 +++++ 2 files changed, 6 insertions(+), 0 deletions(-) diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 1674b49..ef8325f 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -891,6 +891,7 @@ module ActiveRecord instances.size == 1 ? instances.first : instances end + private table_name end end diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index f965652..e9c2802 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -254,6 +254,11 @@ class FixturesWithoutInstantiationTest < ActiveRecord::TestCase assert_nil @unknown end + def test_visibility_of_accessor_method + assert_equal false, respond_to?(:topics, false), "should be private method" + assert_equal true, respond_to?(:topics, true), "confirm to respond surely" + end + def test_accessor_methods assert_equal "The First Topic", topics(:first).title assert_equal "Jamis", developers(:jamis).name -- 1.6.4.2 From 5d979de1a95bb6688d13781a7a42e04acdb3b9cb Mon Sep 17 00:00:00 2001 From: Matthew Rudy Jacobs Date: Mon, 5 Jan 2009 01:34:26 +0000 Subject: [PATCH 640/779] when we run rake rails:freeze:edge update the value of the "REVISION" file rather than creating a new file "REVISION_ce706..." each time [#1694 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- railties/builtin/rails_info/rails/info.rb | 2 +- railties/lib/tasks/framework.rake | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/railties/builtin/rails_info/rails/info.rb b/railties/builtin/rails_info/rails/info.rb index 8c858d2..e18fcbb 100644 --- a/railties/builtin/rails_info/rails/info.rb +++ b/railties/builtin/rails_info/rails/info.rb @@ -38,7 +38,7 @@ module Rails def freeze_edge_version if File.exist?(rails_vendor_root) begin - Dir[File.join(rails_vendor_root, 'REVISION_*')].first.scan(/_(\d+)$/).first.first + File.readlines(File.join(rails_vendor_root,'REVISION')).first.strip rescue Dir[File.join(rails_vendor_root, 'TAG_*')].first.scan(/_(.+)$/).first.first rescue 'unknown' end diff --git a/railties/lib/tasks/framework.rake b/railties/lib/tasks/framework.rake index 191c936..5dcdace 100644 --- a/railties/lib/tasks/framework.rake +++ b/railties/lib/tasks/framework.rake @@ -64,7 +64,10 @@ namespace :rails do rm_f goner end - touch "rails/REVISION_#{latest_revision}" + puts "Frozen to git revision #{latest_revision}" + File.open('rails/REVISION', 'w') do |revision| + revision.puts latest_revision + end end puts 'Updating current scripts, javascripts, and configuration settings' -- 1.6.4.2 From ec017e158a0f006ac30bee94f81411a4d74a5227 Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Sun, 16 May 2010 11:49:21 +0200 Subject: [PATCH 641/779] Added assert_attribute_type to clean up GeneratedAttributeTest [#2377 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../test/generators/generated_attribute_test.rb | 38 ++++++++++++++++++++ railties/test/generators/generator_test_helper.rb | 12 ++++++ 2 files changed, 50 insertions(+), 0 deletions(-) create mode 100644 railties/test/generators/generated_attribute_test.rb diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb new file mode 100644 index 0000000..f17faec --- /dev/null +++ b/railties/test/generators/generated_attribute_test.rb @@ -0,0 +1,38 @@ +require 'generators/generator_test_helper' +require 'rails_generator/generated_attribute' + +class GeneratedAttributeTest < GeneratorTestCase + def test_field_type_returns_text_field + %w(integer float decimal string).each do |name| + assert_attribute_type name, :text_field + end + end + + def test_field_type_returns_datetime_select + %w(datetime timestamp).each do |name| + assert_attribute_type name, :datetime_select + end + end + + def test_field_type_returns_time_select + assert_attribute_type 'time', :time_select + end + + def test_field_type_returns_date_select + assert_attribute_type 'date', :date_select + end + + def test_field_type_returns_text_area + assert_attribute_type 'text', :text_area + end + + def test_field_type_returns_check_box + assert_attribute_type 'boolean', :check_box + end + + def test_field_type_with_unknown_type_returns_text_field + %w(foo bar baz).each do |name| + assert_attribute_type name, :text_field + end + end +end diff --git a/railties/test/generators/generator_test_helper.rb b/railties/test/generators/generator_test_helper.rb index 6f02eb3..d5bfa2b 100644 --- a/railties/test/generators/generator_test_helper.rb +++ b/railties/test/generators/generator_test_helper.rb @@ -307,4 +307,16 @@ class GeneratorTestCase < Test::Unit::TestCase def assert_generated_table(body, name) assert_match /create_table :#{name.to_s} do/, body, "should have table #{name.to_s} defined" end + + # Asserts the given field name gets translated to an attribute type + # properly. + # + # assert_attribute_type 'date', :date_select + # + def assert_attribute_type(name, attribute_type) + assert_equal( + Rails::Generator::GeneratedAttribute.new('test', name).field_type, + attribute_type + ) + end end -- 1.6.4.2 From 5b1f4c51cea332e567b25dcb160cd1f983b46b43 Mon Sep 17 00:00:00 2001 From: Wijnand Wiersma Date: Sun, 16 May 2010 14:49:40 +0200 Subject: [PATCH 642/779] Postgresql doesn't allow to change a string type column to a binary type. Skip this test for postgresql for now. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/test/cases/migration_test.rb | 16 +++++++++------- 1 files changed, 9 insertions(+), 7 deletions(-) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index ffb9e21..299e72e 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -823,13 +823,15 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal "Tester", Person.new.first_name end - def test_change_column_type_default_should_change - old_columns = Person.connection.columns(Person.table_name, "#{name} Columns") - assert !old_columns.find { |c| c.name == 'data' } - - assert_nothing_raised do - Person.connection.add_column "people", "data", :string, :default => '' - Person.connection.change_column "people", "data", :binary + unless current_adapter?(:PostgreSQLAdapter) + def test_change_column_type_default_should_change + old_columns = Person.connection.columns(Person.table_name, "#{name} Columns") + assert !old_columns.find { |c| c.name == 'data' } + + assert_nothing_raised do + Person.connection.add_column "people", "data", :string, :default => '' + Person.connection.change_column "people", "data", :binary + end end end -- 1.6.4.2 From 74206aeff2b6a960612a820ec47e91ef9b778262 Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Sun, 16 May 2010 23:40:17 +0430 Subject: [PATCH 643/779] Rack dependency bumped to 1.1.0, tests passing. [#3558 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/Rakefile | 2 +- actionpack/lib/action_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 23e251f..b982c61 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -80,7 +80,7 @@ spec = Gem::Specification.new do |s| s.requirements << 'none' s.add_dependency('activesupport', '= 2.3.6' + PKG_BUILD) - s.add_dependency('rack', '~> 1.0.0') + s.add_dependency('rack', '~> 1.1.0') s.require_path = 'lib' s.autorequire = 'action_controller' diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 2824ae9..262faea 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -31,7 +31,7 @@ rescue LoadError end end -gem 'rack', '~> 1.0.1' +gem 'rack', '~> 1.1.0' require 'rack' require 'action_controller/cgi_ext' -- 1.6.4.2 From cd3d30d569958a0c3594711f2caf618d8b81249d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 16 May 2010 13:35:42 -0700 Subject: [PATCH 644/779] CI: bump rack to 1.1 --- ci/geminstaller.yml | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/ci/geminstaller.yml b/ci/geminstaller.yml index 4f0c6a2..382b653 100644 --- a/ci/geminstaller.yml +++ b/ci/geminstaller.yml @@ -15,7 +15,7 @@ gems: - name: pg version: >= 0.8.0 - name: rack - version: '~> 1.0.1' + version: '~> 1.1.0' - name: rake version: >= 0.8.1 - name: sqlite-ruby -- 1.6.4.2 From 76608b13d214ff5018565dd51767f6a959a51efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 17 May 2010 12:36:57 +0200 Subject: [PATCH 645/779] superclass_delegating_accessor does not accept options. --- .../abstract/connection_specification.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index 947367f..7e58b1d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -10,7 +10,7 @@ module ActiveRecord ## # :singleton-method: # The connection handler - superclass_delegating_accessor :connection_handler, :instance_writer => false + superclass_delegating_accessor :connection_handler self.connection_handler = ConnectionAdapters::ConnectionHandler.new # Returns the connection currently associated with the class. This can -- 1.6.4.2 From 32b0b5f7b25a05179981d97d6b47aa4a1c683f2f Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Mon, 17 May 2010 01:49:39 +0430 Subject: [PATCH 646/779] Deprecate Array#rand in favor of Array#random_element [#4555 stated:committed] Signed-off-by: Xavier Noria --- .../associations/eager_load_nested_include_test.rb | 12 ++++++------ activerecord/test/cases/named_scope_test.rb | 2 +- activesupport/CHANGELOG | 2 ++ .../active_support/core_ext/array/random_access.rb | 12 +++++++++++- activesupport/test/core_ext/array_ext_test.rb | 12 ++++++++---- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb index 677226e..2d45f6d 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -81,14 +81,14 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase [Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!) end 1.upto(NUM_SIMPLE_OBJS) do - PaintColor.create!(:non_poly_one_id => NonPolyOne.rand.id) - PaintTexture.create!(:non_poly_two_id => NonPolyTwo.rand.id) + PaintColor.create!(:non_poly_one_id => NonPolyOne.random_element.id) + PaintTexture.create!(:non_poly_two_id => NonPolyTwo.random_element.id) end 1.upto(NUM_SHAPE_EXPRESSIONS) do - shape_type = [Circle, Square, Triangle].rand - paint_type = [PaintColor, PaintTexture].rand - ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.rand.id, - :paint_type => paint_type.to_s, :paint_id => paint_type.rand.id) + shape_type = [Circle, Square, Triangle].random_element + paint_type = [PaintColor, PaintTexture].random_element + ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.random_element.id, + :paint_type => paint_type.to_s, :paint_id => paint_type.random_element.id) end end diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 208a2ee..5138173 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -265,7 +265,7 @@ class NamedScopeTest < ActiveRecord::TestCase end def test_rand_should_select_a_random_object_from_proxy - assert Topic.approved.rand.is_a?(Topic) + assert Topic.approved.random_element.is_a?(Topic) end def test_should_use_where_in_query_for_named_scope diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 39d7b25..02f5f20 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *2.3.6 (pending)* +* Deprecated Array#rand in favor of Array#random_element. [Santiago Pastorino, Rizwan Reza] + * Added Object#presence that returns the object if it's #present? otherwise returns nil [DHH/Colin Kelley] * New assertions assert_blank and assert_present. #4299 [Juanjo Bazan] diff --git a/activesupport/lib/active_support/core_ext/array/random_access.rb b/activesupport/lib/active_support/core_ext/array/random_access.rb index 54d17cb..2ec1144 100644 --- a/activesupport/lib/active_support/core_ext/array/random_access.rb +++ b/activesupport/lib/active_support/core_ext/array/random_access.rb @@ -2,8 +2,18 @@ module ActiveSupport #:nodoc: module CoreExtensions #:nodoc: module Array #:nodoc: module RandomAccess + # This method is deprecated because it masks Kernel#rand within the Array class itself, + # which may be used by a 3rd party library extending Array in turn. See + # + # https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4555 + # + def rand # :nodoc: + ActiveSupport::Deprecation.warn 'Array#rand is deprecated and will be removed in Rails 3. Use "random_element" instead', caller + random_element + end + # Returns a random element from the array. - def rand + def random_element self[Kernel.rand(length)] end end diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index f354ade..b17f700 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -323,15 +323,19 @@ class ArrayExtractOptionsTests < Test::Unit::TestCase end end -class ArrayExtRandomTests < Test::Unit::TestCase +class ArrayExtRandomTests < ActiveSupport::TestCase def test_random_element_from_array - assert_nil [].rand + assert_nil [].random_element Kernel.expects(:rand).with(1).returns(0) - assert_equal 'x', ['x'].rand + assert_equal 'x', ['x'].random_element Kernel.expects(:rand).with(3).returns(1) - assert_equal 2, [1, 2, 3].rand + assert_equal 2, [1, 2, 3].random_element + end + + def test_deprecated_rand_on_array + assert_deprecated { [].rand } end end -- 1.6.4.2 From bb2327d9abdb03c7b6e15f30467c801058183ed2 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 18 May 2010 00:24:58 +0200 Subject: [PATCH 647/779] method rename to fix a broken test --- .../associations/eager_load_nested_include_test.rb | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb index 2d45f6d..6e6d057 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -4,6 +4,7 @@ require 'models/author' require 'models/comment' require 'models/category' require 'models/categorization' +require 'active_support/core_ext/array/random_access' module Remembered def self.included(base) @@ -17,7 +18,7 @@ module Remembered module ClassMethods def remembered; @@remembered ||= []; end - def rand; @@remembered.rand; end + def random_element; @@remembered.random_element; end end end -- 1.6.4.2 From 94878c61a329891eb904ace5b06dbc50831869d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 18 May 2010 03:25:57 +0200 Subject: [PATCH 648/779] Deprecate legacy CGI options in SessionStores. --- .../action_controller/session/abstract_store.rb | 6 ++++++ .../lib/action_controller/session/cookie_store.rb | 6 ++++++ 2 files changed, 12 insertions(+), 0 deletions(-) diff --git a/actionpack/lib/action_controller/session/abstract_store.rb b/actionpack/lib/action_controller/session/abstract_store.rb index f6369ab..7a20557 100644 --- a/actionpack/lib/action_controller/session/abstract_store.rb +++ b/actionpack/lib/action_controller/session/abstract_store.rb @@ -98,12 +98,18 @@ module ActionController # Process legacy CGI options options = options.symbolize_keys if options.has_key?(:session_path) + ActiveSupport::Deprecation.warn "Giving :session_path to SessionStore is deprecated, " << + "please use :path instead", caller options[:path] = options.delete(:session_path) end if options.has_key?(:session_key) + ActiveSupport::Deprecation.warn "Giving :session_key to SessionStore is deprecated, " << + "please use :key instead", caller options[:key] = options.delete(:session_key) end if options.has_key?(:session_http_only) + ActiveSupport::Deprecation.warn "Giving :session_http_only to SessionStore is deprecated, " << + "please use :httponly instead", caller options[:httponly] = options.delete(:session_http_only) end diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb index a2543c1..3d21e81 100644 --- a/actionpack/lib/action_controller/session/cookie_store.rb +++ b/actionpack/lib/action_controller/session/cookie_store.rb @@ -59,12 +59,18 @@ module ActionController # Process legacy CGI options options = options.symbolize_keys if options.has_key?(:session_path) + ActiveSupport::Deprecation.warn "Giving :session_path to SessionStore is deprecated, " << + "please use :path instead", caller options[:path] = options.delete(:session_path) end if options.has_key?(:session_key) + ActiveSupport::Deprecation.warn "Giving :session_key to SessionStore is deprecated, " << + "please use :key instead", caller options[:key] = options.delete(:session_key) end if options.has_key?(:session_http_only) + ActiveSupport::Deprecation.warn "Giving :session_http_only to SessionStore is deprecated, " << + "please use :httponly instead", caller options[:httponly] = options.delete(:session_http_only) end -- 1.6.4.2 From a5696e36c6b49381e84184fcc2f164285a26a166 Mon Sep 17 00:00:00 2001 From: Ian White Date: Tue, 18 May 2010 10:30:11 +0100 Subject: [PATCH 649/779] Nested records (re: autosave) are now updated even when the intermediate parent record is unchanged [#4242] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/active_record/autosave_association.rb | 25 ++++++- activerecord/test/cases/nested_attributes_test.rb | 80 ++++++++++++++++++++ activerecord/test/models/ship.rb | 3 +- activerecord/test/models/ship_part.rb | 4 +- 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 8d40eb5..853336c 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -217,6 +217,12 @@ module ActiveRecord @marked_for_destruction end + # Returns whether or not this record has been changed in any way (including whether + # any of its nested autosave associations are likewise changed) + def changed_for_autosave? + new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave? + end + private # Returns the record for an association collection that should be validated @@ -226,12 +232,27 @@ module ActiveRecord if new_record association elsif autosave - association.target.select { |record| record.new_record? || record.changed? || record.marked_for_destruction? } + association.target.select { |record| record.changed_for_autosave? } else association.target.select { |record| record.new_record? } end end - + + # go through nested autosave associations that are loaded in memory (without loading + # any new ones), and return true if is changed for autosave + def nested_records_changed_for_autosave? + self.class.reflect_on_all_autosave_associations.each do |reflection| + if association = association_instance_get(reflection.name) + if [:belongs_to, :has_one].include?(reflection.macro) + return true if association.target && association.target.changed_for_autosave? + else + association.target.each {|record| return true if record.changed_for_autosave? } + end + end + end + false + end + # Validate the association if :validate or :autosave is # turned on for the association specified by +reflection+. def validate_single_association(reflection) diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index c001db6..2202714 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -1,6 +1,7 @@ require "cases/helper" require "models/pirate" require "models/ship" +require "models/ship_part" require "models/bird" require "models/parrot" require "models/treasure" @@ -731,3 +732,82 @@ class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase assert_equal ['Foo', 'Bar'], @owner.pets.map(&:name) end end + +class TestHasOneAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @pirate = Pirate.create!(:catchphrase => "My baby takes tha mornin' train!") + @ship = @pirate.create_ship(:name => "The good ship Dollypop") + @part = @ship.parts.create!(:name => "Mast") + @trinket = @part.trinkets.create!(:name => "Necklace") + end + + test "when great-grandchild changed in memory, saving parent should save great-grandchild" do + @trinket.name = "changed" + @pirate.save + assert_equal "changed", @trinket.reload.name + end + + test "when great-grandchild changed via attributes, saving parent should save great-grandchild" do + @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :name => "changed"}]}]}} + @pirate.save + assert_equal "changed", @trinket.reload.name + end + + test "when great-grandchild marked_for_destruction via attributes, saving parent should destroy great-grandchild" do + @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :_destroy => true}]}]}} + assert_difference('@part.trinkets.count', -1) { @pirate.save } + end + + test "when great-grandchild added via attributes, saving parent should create great-grandchild" do + @pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:name => "created"}]}]}} + assert_difference('@part.trinkets.count', 1) { @pirate.save } + end + + test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do + @trinket.name = "changed" + Ship.create!(:pirate => @pirate, :name => "The Black Rock") + ShipPart.create!(:ship => @ship, :name => "Stern") + assert_no_queries { @pirate.valid? } + end +end + +class TestHasManyAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + @ship = Ship.create!(:name => "The good ship Dollypop") + @part = @ship.parts.create!(:name => "Mast") + @trinket = @part.trinkets.create!(:name => "Necklace") + end + + test "when grandchild changed in memory, saving parent should save grandchild" do + @trinket.name = "changed" + @ship.save + assert_equal "changed", @trinket.reload.name + end + + test "when grandchild changed via attributes, saving parent should save grandchild" do + @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :name => "changed"}]}]} + @ship.save + assert_equal "changed", @trinket.reload.name + end + + test "when grandchild marked_for_destruction via attributes, saving parent should destroy grandchild" do + @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :_destroy => true}]}]} + assert_difference('@part.trinkets.count', -1) { @ship.save } + end + + test "when grandchild added via attributes, saving parent should create grandchild" do + @ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:name => "created"}]}]} + assert_difference('@part.trinkets.count', 1) { @ship.save } + end + + test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do + @trinket.name = "changed" + Ship.create!(:name => "The Black Rock") + ShipPart.create!(:ship => @ship, :name => "Stern") + assert_no_queries { @ship.valid? } + end +end \ No newline at end of file diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb index 75c792d..3da0319 100644 --- a/activerecord/test/models/ship.rb +++ b/activerecord/test/models/ship.rb @@ -3,8 +3,9 @@ class Ship < ActiveRecord::Base belongs_to :pirate belongs_to :update_only_pirate, :class_name => 'Pirate' - has_many :parts, :class_name => 'ShipPart', :autosave => true + has_many :parts, :class_name => 'ShipPart' + accepts_nested_attributes_for :parts, :allow_destroy => true accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } accepts_nested_attributes_for :update_only_pirate, :update_only => true diff --git a/activerecord/test/models/ship_part.rb b/activerecord/test/models/ship_part.rb index 0a606db..8e61d70 100644 --- a/activerecord/test/models/ship_part.rb +++ b/activerecord/test/models/ship_part.rb @@ -1,5 +1,7 @@ class ShipPart < ActiveRecord::Base belongs_to :ship - + has_many :trinkets, :class_name => "Treasure", :as => :looter + accepts_nested_attributes_for :trinkets, :allow_destroy => true + validates_presence_of :name end \ No newline at end of file -- 1.6.4.2 From 99bcce7ec1e7f59db3e6f1e1d3cd02e15eb59602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?E=CC=81tienne=20Barrie=CC=81?= Date: Sun, 16 May 2010 18:50:25 +0200 Subject: [PATCH 650/779] make add_index and remove_index more resilient; new rename_index method; track database limits [#3452 state:committed] Signed-off-by: Jeremy Kemper --- .../abstract/database_limits.rb | 57 ++++++++++++++++++++ .../abstract/schema_statements.rb | 45 +++++++++++++-- .../connection_adapters/abstract_adapter.rb | 2 + .../connection_adapters/postgresql_adapter.rb | 9 ++- .../connection_adapters/sqlite_adapter.rb | 4 +- .../test/cases/active_schema_test_mysql.rb | 5 ++ activerecord/test/cases/migration_test.rb | 34 ++++++++++++ activerecord/test/cases/schema_test_postgresql.rb | 4 +- 8 files changed, 147 insertions(+), 13 deletions(-) create mode 100644 activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb new file mode 100644 index 0000000..4118ea7 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb @@ -0,0 +1,57 @@ +module ActiveRecord + module ConnectionAdapters # :nodoc: + module DatabaseLimits + + # the maximum length of a table alias + def table_alias_length + 255 + end + + # the maximum length of a column name + def column_name_length + 64 + end + + # the maximum length of a table name + def table_name_length + 64 + end + + # the maximum length of an index name + def index_name_length + 64 + end + + # the maximum number of columns per table + def columns_per_table + 1024 + end + + # the maximum number of indexes per table + def indexes_per_table + 16 + end + + # the maximum number of columns in a multicolumn index + def columns_per_multicolumn_index + 16 + end + + # the maximum number of elements in an IN (x,y,z) clause + def in_clause_length + 65535 + end + + # the maximum length of a SQL query + def sql_query_length + 1048575 + end + + # maximum number of joins in a single query + def joins_per_query + 256 + end + + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 211cd7e..4fa5881 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -8,11 +8,6 @@ module ActiveRecord {} end - # This is the maximum length a table alias can be - def table_alias_length - 255 - end - # Truncates a table alias according to the limits of the current adapter. def table_alias_for(table_name) table_name[0..table_alias_length-1].gsub(/\./, '_') @@ -283,6 +278,14 @@ module ActiveRecord index_type = options end + if index_name.length > index_name_length + @logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.") + return + end + if index_exists?(table_name, index_name, false) + @logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.") + return + end quoted_column_names = quoted_columns_for_index(column_names, options).join(", ") execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})" @@ -299,7 +302,28 @@ module ActiveRecord # Remove the index named by_branch_party in the accounts table. # remove_index :accounts, :name => :by_branch_party def remove_index(table_name, options = {}) - execute "DROP INDEX #{quote_column_name(index_name(table_name, options))} ON #{quote_table_name(table_name)}" + index_name = index_name(table_name, options) + unless index_exists?(table_name, index_name, true) + @logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.") + return + end + remove_index!(table_name, index_name) + end + + def remove_index!(table_name, index_name) #:nodoc: + execute "DROP INDEX #{quote_column_name(index_name)} ON #{table_name}" + end + + # Rename an index. + # + # Rename the index_people_on_last_name index to index_users_on_last_name + # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name' + def rename_index(table_name, old_name, new_name) + # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance) + old_index_def = indexes(table_name).detect { |i| i.name == old_name } + return unless old_index_def + remove_index(table_name, :name => old_name) + add_index(table_name, old_index_def.columns, :name => new_name, :unique => old_index_def.unique) end def index_name(table_name, options) #:nodoc: @@ -316,6 +340,15 @@ module ActiveRecord end end + # Verify the existence of an index. + # + # The default argument is returned if the underlying implementation does not define the indexes method, + # as there's no way to determine the correct answer in that case. + def index_exists?(table_name, index_name, default) + return default unless respond_to?(:indexes) + indexes(table_name).detect { |i| i.name == index_name } + end + # Returns a string of CREATE TABLE SQL statement(s) for recreating the # entire structure of the database. def structure_dump diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 22871f2..b225776 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -11,6 +11,7 @@ require 'active_record/connection_adapters/abstract/quoting' require 'active_record/connection_adapters/abstract/connection_pool' require 'active_record/connection_adapters/abstract/connection_specification' require 'active_record/connection_adapters/abstract/query_cache' +require 'active_record/connection_adapters/abstract/database_limits' module ActiveRecord module ConnectionAdapters # :nodoc: @@ -29,6 +30,7 @@ module ActiveRecord # notably, the instance methods provided by SchemaStatement are very useful. class AbstractAdapter include Quoting, DatabaseStatements, SchemaStatements + include DatabaseLimits include QueryCache include ActiveSupport::Callbacks define_callbacks :checkout, :checkin diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 87b2ef6..a348318 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -859,9 +859,12 @@ module ActiveRecord execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}" end - # Drops an index from a table. - def remove_index(table_name, options = {}) - execute "DROP INDEX #{quote_table_name(index_name(table_name, options))}" + def remove_index!(table_name, index_name) #:nodoc: + execute "DROP INDEX #{quote_table_name(index_name)}" + end + + def index_name_length + 63 end # Maps logical Rails types to PostgreSQL-specific data types. diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 0bf97a9..f82e5bf 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -244,8 +244,8 @@ module ActiveRecord column ? column['name'] : nil end - def remove_index(table_name, options={}) #:nodoc: - execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}" + def remove_index!(table_name, index_name) #:nodoc: + execute "DROP INDEX #{quote_column_name(index_name)}" end def rename_table(name, new_name) diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb index f4d123b..3526f49 100644 --- a/activerecord/test/cases/active_schema_test_mysql.rb +++ b/activerecord/test/cases/active_schema_test_mysql.rb @@ -16,6 +16,10 @@ class ActiveSchemaTest < ActiveRecord::TestCase end def test_add_index + # add_index calls index_exists? which can't work since execute is stubbed + ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_exists?) do |*| + false + end expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" assert_equal expected, add_index(:people, :last_name, :length => nil) @@ -30,6 +34,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))" assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10}) + ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:remove_method, :index_exists?) end def test_drop_table diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 299e72e..a730f83 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -119,6 +119,40 @@ if ActiveRecord::Base.connection.supports_migrations? end end + def test_add_index_length_limit + good_index_name = 'x' * Person.connection.index_name_length + too_long_index_name = good_index_name + 'x' + assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => too_long_index_name) } + assert !Person.connection.index_exists?("people", too_long_index_name, false) + assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => good_index_name) } + assert Person.connection.index_exists?("people", good_index_name, false) + end + + def test_remove_nonexistent_index + # we do this by name, so OpenBase is a wash as noted above + unless current_adapter?(:OpenBaseAdapter) + assert_nothing_raised { Person.connection.remove_index("people", "no_such_index") } + end + end + + def test_rename_index + unless current_adapter?(:OpenBaseAdapter) + # keep the names short to make Oracle and similar behave + Person.connection.add_index('people', [:first_name], :name => 'old_idx') + assert_nothing_raised { Person.connection.rename_index('people', 'old_idx', 'new_idx') } + # if the adapter doesn't support the indexes call, pick defaults that let the test pass + assert !Person.connection.index_exists?('people', 'old_idx', false) + assert Person.connection.index_exists?('people', 'new_idx', true) + end + end + + def test_double_add_index + unless current_adapter?(:OpenBaseAdapter) + Person.connection.add_index('people', [:first_name], :name => 'some_idx') + assert_nothing_raised { Person.connection.add_index('people', [:first_name], :name => 'some_idx') } + end + end + def testing_table_with_only_foo_attribute Person.connection.create_table :testings, :id => false do |t| t.column :foo, :string diff --git a/activerecord/test/cases/schema_test_postgresql.rb b/activerecord/test/cases/schema_test_postgresql.rb index a294848..96d325b 100644 --- a/activerecord/test/cases/schema_test_postgresql.rb +++ b/activerecord/test/cases/schema_test_postgresql.rb @@ -137,11 +137,11 @@ class SchemaTest < ActiveRecord::TestCase def test_with_uppercase_index_name ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" - assert_nothing_raised { ActiveRecord::Base.connection.remove_index :things, :name => "#{SCHEMA_NAME}.things_Index"} + assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "#{SCHEMA_NAME}.things_Index"} ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)" ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME - assert_nothing_raised { ActiveRecord::Base.connection.remove_index :things, :name => "things_Index"} + assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "things_Index"} ActiveRecord::Base.connection.schema_search_path = "public" end -- 1.6.4.2 From 9d99e610be0da6e4b682ebb8cc410cdca1ca633a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 18 May 2010 11:03:59 -0700 Subject: [PATCH 651/779] Revert "Don't carry default value when changing column for a binary type on MySQL" Broke mysql tests. This reverts commit ddadcc7cf8332415ba48b66904ee6136b43dda6b. Conflicts: activerecord/test/cases/migration_test.rb [#3234 state:open] --- .../connection_adapters/mysql_adapter.rb | 6 +----- activerecord/test/cases/migration_test.rb | 12 ------------ 2 files changed, 1 insertions(+), 17 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 2c8f08a..69e97b9 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -506,7 +506,7 @@ module ActiveRecord def change_column(table_name, column_name, type, options = {}) #:nodoc: column = column_for(table_name, column_name) - if has_default?(type) && !options_include_default?(options) + unless options_include_default?(options) options[:default] = column.default end @@ -655,10 +655,6 @@ module ActiveRecord end column end - - def has_default?(sql_type) - sql_type =~ :binary || sql_type == :text #mysql forbids defaults on blob and text columns - end end end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index a730f83..8517fe2 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -857,18 +857,6 @@ if ActiveRecord::Base.connection.supports_migrations? assert_equal "Tester", Person.new.first_name end - unless current_adapter?(:PostgreSQLAdapter) - def test_change_column_type_default_should_change - old_columns = Person.connection.columns(Person.table_name, "#{name} Columns") - assert !old_columns.find { |c| c.name == 'data' } - - assert_nothing_raised do - Person.connection.add_column "people", "data", :string, :default => '' - Person.connection.change_column "people", "data", :binary - end - end - end - def test_change_column_quotes_column_names Person.connection.create_table :testings do |t| t.column :select, :string -- 1.6.4.2 From 25ec61330b08975ba01577cde5e59aea04e7926b Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Wed, 19 May 2010 00:59:02 +0200 Subject: [PATCH 652/779] 1.9 compat: deprecated last_(month|year) in favor of prev_(month|year) --- activesupport/CHANGELOG | 2 ++ .../active_support/core_ext/date/calculations.rb | 18 ++++++++++++++---- .../active_support/core_ext/time/calculations.rb | 13 +++++++++++-- activesupport/test/core_ext/date_ext_test.rb | 18 +++++++++++++----- activesupport/test/core_ext/date_time_ext_test.rb | 8 ++++---- activesupport/test/core_ext/time_ext_test.rb | 18 +++++++++++++----- 6 files changed, 57 insertions(+), 20 deletions(-) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 02f5f20..e2988d9 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *2.3.6 (pending)* +* 1.9 compat: deprecated last_(month|year) in favor of prev_(month|year). [fxn] + * Deprecated Array#rand in favor of Array#random_element. [Santiago Pastorino, Rizwan Reza] * Added Object#presence that returns the object if it's #present? otherwise returns nil [DHH/Colin Kelley] diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index fa7f618..4c141b6 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -135,20 +135,30 @@ module ActiveSupport #:nodoc: advance(:years => years) end + def last_year # :nodoc: + ActiveSupport::Deprecation.warn("Date#last_year is deprecated and has been removed in Rails 3, please use Date#prev_year instead", caller) + prev_year + end + # Short-hand for years_ago(1) - def last_year + def prev_year years_ago(1) - end + end unless method_defined?(:prev_year) # Short-hand for years_since(1) def next_year years_since(1) end + def last_month # :nodoc: + ActiveSupport::Deprecation.warn("Date#last_month is deprecated and has been removed in Rails 3, please use Date#prev_month instead", caller) + prev_month + end + # Short-hand for months_ago(1) - def last_month + def prev_month months_ago(1) - end + end unless method_defined?(:prev_month) # Short-hand for months_since(1) def next_month diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 754e00d..a673b55 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -157,8 +157,13 @@ module ActiveSupport #:nodoc: advance(:years => years) end + def last_year # :nodoc: + ActiveSupport::Deprecation.warn("Time#last_year is deprecated and has been removed in Rails 3, please use Time#prev_year instead", caller) + prev_year + end + # Short-hand for years_ago(1) - def last_year + def prev_year years_ago(1) end @@ -167,9 +172,13 @@ module ActiveSupport #:nodoc: years_since(1) end + def last_month # :nodoc: + ActiveSupport::Deprecation.warn("Time#last_month is deprecated and has been removed in Rails 3, please use Time#prev_month instead", caller) + prev_month + end # Short-hand for months_ago(1) - def last_month + def prev_month months_ago(1) end diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 2d2bbf1..3b6eb86 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' -class DateExtCalculationsTest < Test::Unit::TestCase +class DateExtCalculationsTest < ActiveSupport::TestCase def test_to_s date = Date.new(2005, 2, 21) assert_equal "2005-02-21", date.to_s @@ -130,8 +130,12 @@ class DateExtCalculationsTest < Test::Unit::TestCase assert_equal Date.new(2005,2,28), Date.new(2004,2,29).years_since(1) # 1 year since leap day end - def test_last_year - assert_equal Date.new(2004,6,5), Date.new(2005,6,5).last_year + def test_last_year_is_deprecated + assert_deprecated { Date.today.last_year } + end + + def test_prev_year + assert_equal Date.new(2004,6,5), Date.new(2005,6,5).prev_year end def test_next_year @@ -170,8 +174,12 @@ class DateExtCalculationsTest < Test::Unit::TestCase assert_equal Date.new(2005, 9, 30), Date.new(2005, 8, 31).next_month end - def test_last_month_on_31st - assert_equal Date.new(2004, 2, 29), Date.new(2004, 3, 31).last_month + def test_last_month_is_deprecated + assert_deprecated { Date.today.last_month } + end + + def test_prev_month_on_31st + assert_equal Date.new(2004, 2, 29), Date.new(2004, 3, 31).prev_month end def test_yesterday_constructor diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 2506b77..fccf130 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -121,8 +121,8 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_equal DateTime.civil(2005,2,28,10), DateTime.civil(2004,2,29,10,0,0).years_since(1) # 1 year since leap day end - def test_last_year - assert_equal DateTime.civil(2004,6,5,10), DateTime.civil(2005,6,5,10,0,0).last_year + def test_prev_year + assert_equal DateTime.civil(2004,6,5,10), DateTime.civil(2005,6,5,10,0,0).prev_year end def test_next_year @@ -194,8 +194,8 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_equal DateTime.civil(2005, 9, 30), DateTime.civil(2005, 8, 31).next_month end - def test_last_month_on_31st - assert_equal DateTime.civil(2004, 2, 29), DateTime.civil(2004, 3, 31).last_month + def test_prev_month_on_31st + assert_equal DateTime.civil(2004, 2, 29), DateTime.civil(2004, 3, 31).prev_month end def test_xmlschema diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index ff220d4..d24e89b 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' -class TimeExtCalculationsTest < Test::Unit::TestCase +class TimeExtCalculationsTest < ActiveSupport::TestCase def test_seconds_since_midnight assert_equal 1,Time.local(2005,1,1,0,0,1).seconds_since_midnight assert_equal 60,Time.local(2005,1,1,0,1,0).seconds_since_midnight @@ -165,8 +165,12 @@ class TimeExtCalculationsTest < Test::Unit::TestCase # assert_equal Time.local(2182,6,5,10), Time.local(2005,6,5,10,0,0).years_since(177) end - def test_last_year - assert_equal Time.local(2004,6,5,10), Time.local(2005,6,5,10,0,0).last_year + def test_last_year_is_deprecated + assert_deprecated { Time.now.last_year } + end + + def test_prev_year + assert_equal Time.local(2004,6,5,10), Time.local(2005,6,5,10,0,0).prev_year end def test_next_year @@ -562,12 +566,16 @@ class TimeExtCalculationsTest < Test::Unit::TestCase end end + def test_last_month_is_deprecated + assert_deprecated { Time.now.last_month } + end + def test_next_month_on_31st assert_equal Time.local(2005, 9, 30), Time.local(2005, 8, 31).next_month end - def test_last_month_on_31st - assert_equal Time.local(2004, 2, 29), Time.local(2004, 3, 31).last_month + def test_prev_month_on_31st + assert_equal Time.local(2004, 2, 29), Time.local(2004, 3, 31).prev_month end def test_xmlschema_is_available -- 1.6.4.2 From a637b5f4471be6b7284673fc7196308afb265e1d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sat, 22 May 2010 14:18:41 -0400 Subject: [PATCH 653/779] backporting beda2d43 for newer sqlite-ruby bindings Signed-off-by: Xavier Noria --- activerecord/test/cases/calculations_test.rb | 2 +- activerecord/test/cases/query_cache_test.rb | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 60f41a8..503b70a 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -297,7 +297,7 @@ class CalculationsTest < ActiveRecord::TestCase end def test_should_sum_expression - assert_equal '636', Account.sum("2 * credit_limit") + assert_equal 636, Account.sum("2 * credit_limit").to_i end def test_count_with_from_option diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index f90a66d..f7f9777 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -49,8 +49,14 @@ class QueryCacheTest < ActiveRecord::TestCase end def test_cache_does_not_wrap_string_results_in_arrays + require 'sqlite3/version' if current_adapter?(:SQLite3Adapter) + Task.cache do - assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") + if current_adapter?(:SQLite3Adapter) && SQLite3::Version::VERSION > '1.2.5' + assert_instance_of Fixnum, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") + else + assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") + end end end end -- 1.6.4.2 From d8f0a58dcb12de11ea62f286b831b1e758f66392 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 22 May 2010 23:46:03 -0700 Subject: [PATCH 654/779] Remove miscommit from 57337cd --- activesupport/CHANGELOG | 4 ---- 1 files changed, 0 insertions(+), 4 deletions(-) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index e2988d9..36215ac 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -18,10 +18,6 @@ * YAML serialization for OrderedHash. #3608 [Gregor Schmidt] -* Update bundled TZInfo to v0.3.16 [Geoff Buesing] - -* Georgetown TimeZone is now mapped to "America/Guyana" instead of "America/Argentina/San_Juan" #1821 [Geoff Buesing, Reuben Sivan] - * Add Enumerable#exclude? to bring parity to Enumerable#include? and avoid if !x.include?/else calls [DHH] -- 1.6.4.2 From 56bb5504dd1fc7314f1bd7a489ddaff973668be2 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 22 May 2010 23:53:40 -0700 Subject: [PATCH 655/779] 2.3.6 release --- actionmailer/CHANGELOG | 2 +- actionpack/CHANGELOG | 2 +- activerecord/CHANGELOG | 2 +- activeresource/CHANGELOG | 5 +++++ activesupport/CHANGELOG | 2 +- railties/CHANGELOG | 7 +++++-- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 1088537..6a8671f 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,4 +1,4 @@ -*2.3.6 (pending)* +*2.3.6 (May 23, 2010)* * Upgrade TMail from 1.2.3 to 1.2.7. [Mikel Lindsaar] diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index ed1e24d..ed43971 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,4 +1,4 @@ -*2.3.6 (unreleased)* +*2.3.6 (May 23, 2010)* * JSON: set Base.include_root_in_json = true to include a root value in the JSON: {"post": {"title": ...}}. Mirrors the Active Record option. #2584 [Matthew Moore, Joe Martinez, Elad Meidar, Santiago Pastorino] diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 8726874..dcc392e 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,4 @@ -*2.3.6 (pending)* +*2.3.6 (May 23, 2010)* * Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik] diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index 272c0ab..edd4554 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.6 (May 23, 2010)* + +* No changes, just a version bump. + + *2.3.5 (November 25, 2009)* * Minor Bug Fixes and deprecation warnings diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 36215ac..f5d2c9d 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,4 +1,4 @@ -*2.3.6 (pending)* +*2.3.6 (May 23, 2010)* * 1.9 compat: deprecated last_(month|year) in favor of prev_(month|year). [fxn] diff --git a/railties/CHANGELOG b/railties/CHANGELOG index f2db66a..9e8c1f3 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,11 +1,14 @@ -*Edge* +*2.3.6 (May 23, 2010)* * Added config/initializers/cookie_verification_secret.rb with an auto-generated secret for using ActionController::Base#cookies.signed [DHH] * Fixed that the debugger wouldn't go into IRB mode because of left-over ARGVs [DHH] -* 1.9 compatibility +*2.3.5 (November 25, 2009)* + +* Ruby 1.9 compatibility + *2.3.4 (September 4, 2009)* -- 1.6.4.2 From 55e88eeee400294a4c41606bdbaf3284ff22f8f8 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 23 May 2010 01:39:45 -0700 Subject: [PATCH 656/779] Bump 2-3-stable to 2.3.7 --- actionmailer/CHANGELOG | 5 +++++ actionmailer/Rakefile | 2 +- actionmailer/lib/action_mailer/version.rb | 2 +- actionpack/CHANGELOG | 5 +++++ actionpack/Rakefile | 2 +- actionpack/lib/action_pack/version.rb | 2 +- activerecord/CHANGELOG | 5 +++++ activerecord/Rakefile | 2 +- activerecord/lib/active_record/version.rb | 2 +- activeresource/CHANGELOG | 5 +++++ activeresource/Rakefile | 2 +- activeresource/lib/active_resource/version.rb | 2 +- activesupport/CHANGELOG | 5 +++++ activesupport/lib/active_support/version.rb | 2 +- railties/CHANGELOG | 5 +++++ railties/Rakefile | 10 +++++----- railties/lib/rails/version.rb | 2 +- 17 files changed, 45 insertions(+), 15 deletions(-) diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 6a8671f..f779988 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.7 (unreleased)* + +* ... + + *2.3.6 (May 23, 2010)* * Upgrade TMail from 1.2.3 to 1.2.7. [Mikel Lindsaar] diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index 07748ba..977496d 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -54,7 +54,7 @@ spec = Gem::Specification.new do |s| s.rubyforge_project = "actionmailer" s.homepage = "http://www.rubyonrails.org" - s.add_dependency('actionpack', '= 2.3.6' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.7' + PKG_BUILD) s.has_rdoc = true s.requirements << 'none' diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index 08f67e2..9b6ed2b 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -2,7 +2,7 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 6 + TINY = 7 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index ed43971..93d2de1 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.7 (unreleased)* + +* ... + + *2.3.6 (May 23, 2010)* * JSON: set Base.include_root_in_json = true to include a root value in the JSON: {"post": {"title": ...}}. Mirrors the Active Record option. #2584 [Matthew Moore, Joe Martinez, Elad Meidar, Santiago Pastorino] diff --git a/actionpack/Rakefile b/actionpack/Rakefile index b982c61..baf276b 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -79,7 +79,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.3.6' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.7' + PKG_BUILD) s.add_dependency('rack', '~> 1.1.0') s.require_path = 'lib' diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 496f386..30ec17f 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -2,7 +2,7 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 6 + TINY = 7 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index dcc392e..04ad923 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.7 (unreleased)* + +* ... + + *2.3.6 (May 23, 2010)* * Add index length support for MySQL. #1852 [Emili Parreno, Pratik Naik] diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 05f0e23..50026c7 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -192,7 +192,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.6' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.7' + PKG_BUILD) s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite" s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite" diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index f9c8c8e..9bf034c 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,7 +2,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 6 + TINY = 7 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index edd4554..83aa75c 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.7 (unreleased)* + +* ... + + *2.3.6 (May 23, 2010)* * No changes, just a version bump. diff --git a/activeresource/Rakefile b/activeresource/Rakefile index 6cc04eb..710d14d 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -66,7 +66,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.6' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.7' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'active_resource' diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index d25c9a0..7b02b1e 100644 --- a/activeresource/lib/active_resource/version.rb +++ b/activeresource/lib/active_resource/version.rb @@ -2,7 +2,7 @@ module ActiveResource module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 6 + TINY = 7 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index f5d2c9d..dd775ae 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.7 (unreleased)* + +* ... + + *2.3.6 (May 23, 2010)* * 1.9 compat: deprecated last_(month|year) in favor of prev_(month|year). [fxn] diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 054c0ca..200ad4b 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -2,7 +2,7 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 6 + TINY = 7 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 9e8c1f3..f2ac42c 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.7 (unreleased)* + +* ... + + *2.3.6 (May 23, 2010)* * Added config/initializers/cookie_verification_secret.rb with an auto-generated secret for using ActionController::Base#cookies.signed [DHH] diff --git a/railties/Rakefile b/railties/Rakefile index 5654b90..c2c942b 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -314,11 +314,11 @@ spec = Gem::Specification.new do |s| EOF s.add_dependency('rake', '>= 0.8.3') - s.add_dependency('activesupport', '= 2.3.6' + PKG_BUILD) - s.add_dependency('activerecord', '= 2.3.6' + PKG_BUILD) - s.add_dependency('actionpack', '= 2.3.6' + PKG_BUILD) - s.add_dependency('actionmailer', '= 2.3.6' + PKG_BUILD) - s.add_dependency('activeresource', '= 2.3.6' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.7' + PKG_BUILD) + s.add_dependency('activerecord', '= 2.3.7' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.7' + PKG_BUILD) + s.add_dependency('actionmailer', '= 2.3.7' + PKG_BUILD) + s.add_dependency('activeresource', '= 2.3.7' + PKG_BUILD) s.rdoc_options << '--exclude' << '.' s.has_rdoc = false diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index 0fcaef8..ecfcb8e 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -2,7 +2,7 @@ module Rails module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 6 + TINY = 7 STRING = [MAJOR, MINOR, TINY].join('.') end -- 1.6.4.2 From 48fbe7b0d8fcecce200ea35f46a8716077e13aea Mon Sep 17 00:00:00 2001 From: Nathan Weizenbaum Date: Sun, 23 May 2010 14:35:21 -0700 Subject: [PATCH 657/779] Mark all raw HTML being concatted as HTML-safe. Signed-off-by: Jeremy Kemper --- actionpack/lib/action_view/helpers/form_helper.rb | 2 +- .../lib/action_view/helpers/form_tag_helper.rb | 4 ++-- .../lib/action_view/helpers/prototype_helper.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index cd190e9..38c44a2 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -280,7 +280,7 @@ module ActionView concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {})) fields_for(object_name, *(args << options), &proc) - concat('') + concat(''.html_safe) end def apply_form_for_options!(object_or_array, options) #:nodoc: diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 4b589c2..37c9dcd 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -435,7 +435,7 @@ module ActionView concat(tag(:fieldset, options, true)) concat(content_tag(:legend, legend)) unless legend.blank? concat(content) - concat("") + concat("".html_safe) end private @@ -469,7 +469,7 @@ module ActionView content = capture(&block) concat(form_tag_html(html_options)) concat(content) - concat("") + concat("".html_safe) end def token_tag diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 83049da..4016683 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -393,7 +393,7 @@ module ActionView concat(form_remote_tag(options)) fields_for(object_name, *(args << options), &proc) - concat('') + concat(''.html_safe) end alias_method :form_remote_for, :remote_form_for -- 1.6.4.2 From e53791f8c06cad94f69789143e442e9866f9dfe0 Mon Sep 17 00:00:00 2001 From: Nathan Weizenbaum Date: Sun, 23 May 2010 04:10:24 -0700 Subject: [PATCH 658/779] Don't always mark the argument to #concat as HTML-safe. Signed-off-by: Jeremy Kemper --- .../lib/action_controller/caching/fragments.rb | 2 +- actionpack/lib/action_view/helpers/text_helper.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index eefb649..32e2a5d 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -37,7 +37,7 @@ module ActionController #:nodoc: def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc: if perform_caching if cache = read_fragment(name, options) - buffer.safe_concat(cache.html_safe) + buffer.concat(cache.html_safe) else pos = buffer.length block.call diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index f2f1557..df8c93f 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -29,7 +29,7 @@ module ActionView ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed. Please remove it from your views and helpers.", caller) end - output_buffer.safe_concat(string) + output_buffer << string end # Truncates a given +text+ after a given :length if +text+ is longer than :length -- 1.6.4.2 From e3f14d12cdad03a2294c8f7d4e170bbaecefe098 Mon Sep 17 00:00:00 2001 From: Nathan Weizenbaum Date: Sun, 23 May 2010 14:47:31 -0700 Subject: [PATCH 659/779] Don't incompatibly monkeypatch ERB. Signed-off-by: Jeremy Kemper --- .../core_ext/string/output_safety.rb | 16 ---------------- 1 files changed, 0 insertions(+), 16 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 1d779a0..59d17ff 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -1,22 +1,6 @@ require 'erb' class ERB - undef :set_eoutvar - def set_eoutvar(compiler, eoutvar = '_erbout') - compiler.put_cmd = "#{eoutvar}.safe_concat" - compiler.insert_cmd = "#{eoutvar}.safe_concat" - - cmd = [] - cmd.push "#{eoutvar} = ActiveSupport::SafeBuffer.new" - - compiler.pre_cmd = cmd - - cmd = [] - cmd.push(eoutvar) - - compiler.post_cmd = cmd - end - module Util HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' } JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' } -- 1.6.4.2 From ca5f5d97b9b4e24502583c2aa15b35142797e9f3 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 23 May 2010 15:28:40 -0700 Subject: [PATCH 660/779] Fix test rendering unmarked but safe HTML --- actionpack/test/view/test_case_test.rb | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/test/view/test_case_test.rb b/actionpack/test/view/test_case_test.rb index 4b4abb8..ace3ee4 100644 --- a/actionpack/test/view/test_case_test.rb +++ b/actionpack/test/view/test_case_test.rb @@ -159,7 +159,7 @@ module ActionView class AssertionsTest < ActionView::TestCase def render_from_helper form_tag('/foo') do - concat render(:text => '
  • foo
') + concat render(:text => '
  • foo
'.html_safe) end end helper_method :render_from_helper @@ -173,4 +173,4 @@ module ActionView end end end -end \ No newline at end of file +end -- 1.6.4.2 From ab2d7c8b5d424da7b6d1e1d401e2a0a89d28753c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 23 May 2010 16:21:44 -0700 Subject: [PATCH 661/779] Use a non-XSS-protected output buffer for view tests --- actionpack/lib/action_view/test_case.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index fba8f61..61749b2 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -53,7 +53,7 @@ module ActionView setup :setup_with_controller def setup_with_controller @controller = TestController.new - @output_buffer = ActiveSupport::SafeBuffer.new + @output_buffer = ''.html_safe @rendered = '' self.class.send(:include_helper_modules!) -- 1.6.4.2 From 86f028799365dc670c9aeb3386eefb5fceafe433 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 23 May 2010 16:51:47 -0700 Subject: [PATCH 662/779] Revert "Don't always mark the argument to #concat as HTML-safe." This reverts commit e53791f8c06cad94f69789143e442e9866f9dfe0. --- .../lib/action_controller/caching/fragments.rb | 2 +- actionpack/lib/action_view/helpers/text_helper.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 32e2a5d..eefb649 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -37,7 +37,7 @@ module ActionController #:nodoc: def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc: if perform_caching if cache = read_fragment(name, options) - buffer.concat(cache.html_safe) + buffer.safe_concat(cache.html_safe) else pos = buffer.length block.call diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index df8c93f..f2f1557 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -29,7 +29,7 @@ module ActionView ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed. Please remove it from your views and helpers.", caller) end - output_buffer << string + output_buffer.safe_concat(string) end # Truncates a given +text+ after a given :length if +text+ is longer than :length -- 1.6.4.2 From b10bf834b77fa296b7c4a00908eb3696e785b9e3 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 23 May 2010 20:13:26 -0300 Subject: [PATCH 663/779] Make use of safe_concat on TextHelper concat Signed-off-by: Jeremy Kemper --- .../core_ext/string/output_safety.rb | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 59d17ff..48cc11d 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -96,6 +96,7 @@ module ActiveSupport #:nodoc: end class String + alias safe_concat concat alias_method :add_without_safety, :+ def as_str -- 1.6.4.2 From 3ff921a65ae3a1d288148c14b3be17bef1f6bca1 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 23 May 2010 17:19:16 -0700 Subject: [PATCH 664/779] rails_xss handles deprecated String html safety, when installed --- .../core_ext/string/output_safety.rb | 30 +------------------- 1 files changed, 1 insertions(+), 29 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 48cc11d..6fac5e5 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -97,7 +97,6 @@ end class String alias safe_concat concat - alias_method :add_without_safety, :+ def as_str self @@ -108,38 +107,11 @@ class String end def html_safe? - defined?(@_rails_html_safe) + false end def html_safe! ActiveSupport::Deprecation.warn("Use html_safe with your strings instead of html_safe! See http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/ for the full story.", caller) - @_rails_html_safe = true self end - - def add_with_safety(other) - result = add_without_safety(other) - if html_safe? && also_html_safe?(other) - result.html_safe! - else - result - end - end - alias_method :+, :add_with_safety - - def concat_with_safety(other_or_fixnum) - result = concat_without_safety(other_or_fixnum) - unless html_safe? && also_html_safe?(other_or_fixnum) - remove_instance_variable(:@_rails_html_safe) if defined?(@_rails_html_safe) - end - result - end - alias_method_chain :concat, :safety - undef_method :<< - alias_method :<<, :concat_with_safety - - private - def also_html_safe?(other) - other.respond_to?(:html_safe?) && other.html_safe? - end end -- 1.6.4.2 From 60e82a3ca2fe6058292db659c3411794888d1418 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 23 May 2010 22:07:43 -0700 Subject: [PATCH 665/779] Move tests for deprecated String#html_safe! to plugin --- .../core_ext/string/output_safety.rb | 5 - activesupport/test/core_ext/string_ext_test.rb | 105 +------------------- 2 files changed, 2 insertions(+), 108 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 6fac5e5..af26d99 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -109,9 +109,4 @@ class String def html_safe? false end - - def html_safe! - ActiveSupport::Deprecation.warn("Use html_safe with your strings instead of html_safe! See http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/ for the full story.", caller) - self - end end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index af3ec12..27c2c3b 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -298,11 +298,8 @@ class OutputSafetyTest < ActiveSupport::TestCase assert !@string.html_safe? end - test "A string can be marked safe using html_safe!" do - assert_deprecated do - @string.html_safe! - assert @string.html_safe? - end + test "Marking a string html_safe! doesn't work unless rails_xss is installed" do + assert_raise(NoMethodError) { @string.html_safe! } end test "A string can be marked safe" do @@ -310,12 +307,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert string.html_safe? end - test "Marking a string safe returns the string using html_safe!" do - assert_deprecated do - assert_equal @string, @string.html_safe! - end - end - test "Marking a string safe returns the string" do assert_equal @string, @string.html_safe end @@ -336,17 +327,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert string.html_safe? end - test "Adding a safe string to another safe string returns a safe string using html_safe!" do - assert_deprecated do - @other_string = "other".html_safe! - @string.html_safe! - @combination = @other_string + @string - - assert_equal "otherhello", @combination - assert @combination.html_safe? - end - end - test "Adding a safe string to another safe string returns a safe string" do @other_string = "other".html_safe string = @string.html_safe @@ -356,20 +336,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert @combination.html_safe? end - test "Adding an unsafe string to a safe string returns an unsafe string using html_safe!" do - assert_deprecated do - @other_string = "other".html_safe! - @combination = @other_string + "" - @other_combination = @string + "" - - assert_equal "other", @combination - assert_equal "hello", @other_combination - - assert !@combination.html_safe? - assert !@other_combination.html_safe? - end - end - test "Adding an unsafe string to a safe string escapes it and returns a safe string" do @other_string = "other".html_safe @combination = @other_string + "" @@ -382,16 +348,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert !@other_combination.html_safe? end - test "Concatting safe onto unsafe yields unsafe using html_safe!" do - assert_deprecated do - @other_string = "other" - @string.html_safe! - - @other_string.concat(@string) - assert !@other_string.html_safe? - end - end - test "Concatting safe onto unsafe yields unsafe" do @other_string = "other" string = @string.html_safe @@ -400,15 +356,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert !@other_string.html_safe? end - test "Concatting unsafe onto safe yields unsafe using html_safe!" do - assert_deprecated do - @other_string = "other".html_safe! - string = @other_string.concat("") - assert_equal "other", string - assert !string.html_safe? - end - end - test "Concatting unsafe onto safe yields escaped safe" do @other_string = "other".html_safe string = @other_string.concat("") @@ -416,16 +363,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert string.html_safe? end - test "Concatting safe onto safe yields safe using html_safe!" do - assert_deprecated do - @other_string = "other".html_safe! - @string.html_safe! - - @other_string.concat(@string) - assert @other_string.html_safe? - end - end - test "Concatting safe onto safe yields safe" do @other_string = "other".html_safe string = @string.html_safe @@ -434,16 +371,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert @other_string.html_safe? end - test "Concatting safe onto unsafe with << yields unsafe using html_safe!" do - assert_deprecated do - @other_string = "other" - @string.html_safe! - - @other_string << @string - assert !@other_string.html_safe? - end - end - test "Concatting safe onto unsafe with << yields unsafe" do @other_string = "other" string = @string.html_safe @@ -452,15 +379,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert !@other_string.html_safe? end - test "Concatting unsafe onto safe with << yields unsafe using html_safe!" do - assert_deprecated do - @other_string = "other".html_safe! - string = @other_string << "" - assert_equal "other", string - assert !string.html_safe? - end - end - test "Concatting unsafe onto safe with << yields escaped safe" do @other_string = "other".html_safe string = @other_string << "" @@ -468,16 +386,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert string.html_safe? end - test "Concatting safe onto safe with << yields safe using html_safe!" do - assert_deprecated do - @other_string = "other".html_safe! - @string.html_safe! - - @other_string << @string - assert @other_string.html_safe? - end - end - test "Concatting safe onto safe with << yields safe" do @other_string = "other".html_safe @string.html_safe @@ -486,15 +394,6 @@ class OutputSafetyTest < ActiveSupport::TestCase assert @other_string.html_safe? end - test "Concatting a fixnum to safe always yields safe using html_safe!" do - assert_deprecated do - @string.html_safe! - @string.concat(13) - assert_equal "hello".concat(13), @string - assert @string.html_safe? - end - end - test "Concatting a fixnum to safe always yields safe" do string = @string.html_safe string = string.concat(13) -- 1.6.4.2 From 326188c25efa3721a7e2f68cea3534b3e2850fcf Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 23 May 2010 23:36:27 -0700 Subject: [PATCH 666/779] 2.3.7 release: fix rails_xss compatibility --- actionmailer/CHANGELOG | 4 ++-- actionpack/CHANGELOG | 4 ++-- activerecord/CHANGELOG | 4 ++-- activeresource/CHANGELOG | 4 ++-- activesupport/CHANGELOG | 4 ++-- railties/CHANGELOG | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index f779988..3c50919 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.7 (unreleased)* +*2.3.7 (May 24, 2010)* -* ... +* Version bump. *2.3.6 (May 23, 2010)* diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 93d2de1..db3895f 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.7 (unreleased)* +*2.3.7 (May 24, 2010)* -* ... +* HTML safety: fix compatibility with the optional rails_xss plugin. [Nathan Weizenbaum, Santiago Pastorino] *2.3.6 (May 23, 2010)* diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 04ad923..57eab50 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.7 (unreleased)* +*2.3.7 (May 24, 2010)* -* ... +* Version bump. *2.3.6 (May 23, 2010)* diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index 83aa75c..7769dda 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.7 (unreleased)* +*2.3.7 (May 24, 2010)* -* ... +* Version bump. *2.3.6 (May 23, 2010)* diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index dd775ae..e318456 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.7 (unreleased)* +*2.3.7 (May 24, 2010)* -* ... +* HTML safety: fix compatibility with the optional rails_xss plugin. [Nathan Weizenbaum, Santiago Pastorino] *2.3.6 (May 23, 2010)* diff --git a/railties/CHANGELOG b/railties/CHANGELOG index f2ac42c..83ec942 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.7 (unreleased)* +*2.3.7 (May 24, 2010)* -* ... +* Version bump. *2.3.6 (May 23, 2010)* -- 1.6.4.2 From f97da34b4f8a2f00d11e5b7d7847d46e3128e4ab Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 24 May 2010 01:39:19 -0700 Subject: [PATCH 667/779] Bump 2-3-stable to 2.3.8 --- actionmailer/CHANGELOG | 5 +++++ actionmailer/Rakefile | 2 +- actionmailer/lib/action_mailer/version.rb | 2 +- actionpack/CHANGELOG | 5 +++++ actionpack/Rakefile | 2 +- actionpack/lib/action_pack/version.rb | 2 +- activerecord/CHANGELOG | 5 +++++ activerecord/Rakefile | 2 +- activerecord/lib/active_record/version.rb | 2 +- activeresource/CHANGELOG | 5 +++++ activeresource/Rakefile | 2 +- activeresource/lib/active_resource/version.rb | 2 +- activesupport/CHANGELOG | 5 +++++ activesupport/lib/active_support/version.rb | 2 +- railties/CHANGELOG | 5 +++++ railties/Rakefile | 10 +++++----- railties/lib/rails/version.rb | 2 +- 17 files changed, 45 insertions(+), 15 deletions(-) diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 3c50919..73f1aa5 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.8 (unreleased)* + +* ... + + *2.3.7 (May 24, 2010)* * Version bump. diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index 977496d..4d1807a 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -54,7 +54,7 @@ spec = Gem::Specification.new do |s| s.rubyforge_project = "actionmailer" s.homepage = "http://www.rubyonrails.org" - s.add_dependency('actionpack', '= 2.3.7' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.8' + PKG_BUILD) s.has_rdoc = true s.requirements << 'none' diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index 9b6ed2b..6ad7a26 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -2,7 +2,7 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 7 + TINY = 8 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index db3895f..66d1093 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.8 (unreleased)* + +* ... + + *2.3.7 (May 24, 2010)* * HTML safety: fix compatibility with the optional rails_xss plugin. [Nathan Weizenbaum, Santiago Pastorino] diff --git a/actionpack/Rakefile b/actionpack/Rakefile index baf276b..c0ebd0a 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -79,7 +79,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.3.7' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.8' + PKG_BUILD) s.add_dependency('rack', '~> 1.1.0') s.require_path = 'lib' diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 30ec17f..a464dda 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -2,7 +2,7 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 7 + TINY = 8 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 57eab50..321b49c 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.8 (unreleased)* + +* ... + + *2.3.7 (May 24, 2010)* * Version bump. diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 50026c7..6dd6989 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -192,7 +192,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.7' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.8' + PKG_BUILD) s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite" s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite" diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 9bf034c..d6a7f60 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,7 +2,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 7 + TINY = 8 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index 7769dda..738df13 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.8 (unreleased)* + +* ... + + *2.3.7 (May 24, 2010)* * Version bump. diff --git a/activeresource/Rakefile b/activeresource/Rakefile index 710d14d..fa11d0c 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -66,7 +66,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.7' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.8' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'active_resource' diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index 7b02b1e..95b3cf9 100644 --- a/activeresource/lib/active_resource/version.rb +++ b/activeresource/lib/active_resource/version.rb @@ -2,7 +2,7 @@ module ActiveResource module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 7 + TINY = 8 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index e318456..abe6f56 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.8 (unreleased)* + +* ... + + *2.3.7 (May 24, 2010)* * HTML safety: fix compatibility with the optional rails_xss plugin. [Nathan Weizenbaum, Santiago Pastorino] diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 200ad4b..ac357e6 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -2,7 +2,7 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 7 + TINY = 8 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 83ec942..2185ec6 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.8 (unreleased)* + +* ... + + *2.3.7 (May 24, 2010)* * Version bump. diff --git a/railties/Rakefile b/railties/Rakefile index c2c942b..350cfb5 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -314,11 +314,11 @@ spec = Gem::Specification.new do |s| EOF s.add_dependency('rake', '>= 0.8.3') - s.add_dependency('activesupport', '= 2.3.7' + PKG_BUILD) - s.add_dependency('activerecord', '= 2.3.7' + PKG_BUILD) - s.add_dependency('actionpack', '= 2.3.7' + PKG_BUILD) - s.add_dependency('actionmailer', '= 2.3.7' + PKG_BUILD) - s.add_dependency('activeresource', '= 2.3.7' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.8' + PKG_BUILD) + s.add_dependency('activerecord', '= 2.3.8' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.8' + PKG_BUILD) + s.add_dependency('actionmailer', '= 2.3.8' + PKG_BUILD) + s.add_dependency('activeresource', '= 2.3.8' + PKG_BUILD) s.rdoc_options << '--exclude' << '.' s.has_rdoc = false diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index ecfcb8e..db11083 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -2,7 +2,7 @@ module Rails module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 7 + TINY = 8 STRING = [MAJOR, MINOR, TINY].join('.') end -- 1.6.4.2 From bd9ca9aed0b5ab3697a7ae27a53664f3118b132c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Sat, 17 Apr 2010 05:13:40 +0200 Subject: [PATCH 668/779] auto_link: support arbitrary URI schemes like "ftp:" and "file:" recognizes all URI scheme allowed characters, such as colon and period. [#3494 state:resolved] --- actionpack/lib/action_view/helpers/text_helper.rb | 6 +++--- actionpack/test/template/text_helper_test.rb | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index f2f1557..954ca7d 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -532,7 +532,7 @@ module ActionView end AUTO_LINK_RE = %r{ - ( https?:// | www\. ) + (?: ([\w+.:-]+:)// | www\. ) [^\s<]+ }x unless const_defined?(:AUTO_LINK_RE) @@ -543,7 +543,7 @@ module ActionView def auto_link_urls(text, html_options = {}) link_attributes = html_options.stringify_keys text.gsub(AUTO_LINK_RE) do - href = $& + scheme, href = $1, $& punctuation = '' left, right = $`, $' # detect already linked URLs and URLs in the middle of a tag @@ -560,7 +560,7 @@ module ActionView end link_text = block_given?? yield(href) : href - href = 'http://' + href unless href =~ %r{^[a-z]+://}i + href = 'http://' + href unless scheme content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 6ee5ae1..6ee7faa 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -368,17 +368,16 @@ class TextHelperTest < ActionView::TestCase end def test_auto_link_other_protocols - silence_warnings do - begin - old_re_value = ActionView::Helpers::TextHelper::AUTO_LINK_RE - ActionView::Helpers::TextHelper.const_set :AUTO_LINK_RE, %r{(ftp://)[^\s<]+} - link_raw = 'ftp://example.com/file.txt' - link_result = generate_result(link_raw) - assert_equal %(Download #{link_result}), auto_link("Download #{link_raw}") - ensure - ActionView::Helpers::TextHelper.const_set :AUTO_LINK_RE, old_re_value - end - end + ftp_raw = 'ftp://example.com/file.txt' + assert_equal %(Download #{generate_result(ftp_raw)}), auto_link("Download #{ftp_raw}") + + file_scheme = 'file:///home/username/RomeoAndJuliet.pdf' + z39_scheme = 'z39.50r://host:696/db' + chrome_scheme = 'chrome://package/section/path' + view_source = 'view-source:http://en.wikipedia.org/wiki/URI_scheme' + assert_equal generate_result(z39_scheme), auto_link(z39_scheme) + assert_equal generate_result(chrome_scheme), auto_link(chrome_scheme) + assert_equal generate_result(view_source), auto_link(view_source) end def test_auto_link_already_linked -- 1.6.4.2 From 17b4fd25e4de8f05d40ccaa776e51636745aa8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Wed, 11 Mar 2009 11:27:44 +0100 Subject: [PATCH 669/779] avoid auto_linking already linked emails; more robust detection of linked URLs References #1523 [#1862 state:resolved] [#3591 state:resolved] Add test that shows how link text can contain HTML if needed: the trick is using block form in combination with `raw`. Let link text be automatically HTML-escaped [#2017 state:resolved] --- actionpack/lib/action_view/helpers/text_helper.rb | 25 +++++++++---- actionpack/test/template/text_helper_test.rb | 37 +++++++++++++++++---- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 954ca7d..6690e02 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -534,7 +534,12 @@ module ActionView AUTO_LINK_RE = %r{ (?: ([\w+.:-]+:)// | www\. ) [^\s<]+ - }x unless const_defined?(:AUTO_LINK_RE) + }x + + # regexps for determining context, used high-volume + AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, //i, /<\/a>/i] + + AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/ BRACKETS = { ']' => '[', ')' => '(', '}' => '{' } @@ -545,9 +550,8 @@ module ActionView text.gsub(AUTO_LINK_RE) do scheme, href = $1, $& punctuation = '' - left, right = $`, $' - # detect already linked URLs and URLs in the middle of a tag - if left =~ /<[^>]+$/ && right =~ /^[^>]*>/ + + if auto_linked?($`, $') # do not change string; URL is already linked href else @@ -570,11 +574,10 @@ module ActionView # Turns all email addresses into clickable links. If a block is given, # each email is yielded and the result is used as the link text. def auto_link_email_addresses(text, html_options = {}) - body = text.dup - text.gsub(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do - text = $1 + text.gsub(AUTO_EMAIL_RE) do + text = $& - if body.match(/]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/) + if auto_linked?($`, $') text else display_text = (block_given?) ? yield(text) : text @@ -582,6 +585,12 @@ module ActionView end end end + + # Detects already linked context or position in the middle of a tag + def auto_linked?(left, right) + (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or + (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3]) + end end end end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 6ee7faa..d40d021 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -296,6 +296,7 @@ class TextHelperTest < ActionView::TestCase assert_equal %(

Link #{link_result_with_options}

), auto_link("

Link #{link_raw}

", :all, {:target => "_blank"}) assert_equal %(Go to #{link_result}.), auto_link(%(Go to #{link_raw}.)) assert_equal %(

Go to #{link_result}, then say hello to #{email_result}.

), auto_link(%(

Go to #{link_raw}, then say hello to #{email_raw}.

)) + assert_equal %(#{link_result} #{link_result}), auto_link(%(#{link_result} #{link_raw})) email2_raw = '+david@loudthinking.com' email2_result = %{#{email2_raw}} @@ -382,9 +383,24 @@ class TextHelperTest < ActionView::TestCase def test_auto_link_already_linked linked1 = generate_result('Ruby On Rails', 'http://www.rubyonrails.com') - linked2 = generate_result('www.rubyonrails.com', 'http://www.rubyonrails.com') + linked2 = %('www.example.com') + linked3 = %('www.example.com') + linked4 = %('www.example.com') + linked5 = %('close www.example.com') assert_equal linked1, auto_link(linked1) assert_equal linked2, auto_link(linked2) + assert_equal linked3, auto_link(linked3) + assert_equal linked4, auto_link(linked4) + assert_equal linked5, auto_link(linked5) + + linked_email = %Q(Mail me) + assert_equal linked_email, auto_link(linked_email) + end + + def test_auto_link_within_tags + link_raw = 'http://www.rubyonrails.org/images/rails.png' + link_result = %Q() + assert_equal link_result, auto_link(link_result) end def test_auto_link_with_brackets @@ -404,12 +420,6 @@ class TextHelperTest < ActionView::TestCase assert_equal "{link: #{link3_result}}", auto_link("{link: #{link3_raw}}") end - def test_auto_link_in_tags - link_raw = 'http://www.rubyonrails.org/images/rails.png' - link_result = %Q() - assert_equal link_result, auto_link(link_result) - end - def test_auto_link_at_eol url1 = "http://api.rubyonrails.com/Foo.html" url2 = "http://www.ruby-doc.org/core/Bar.html" @@ -423,6 +433,19 @@ class TextHelperTest < ActionView::TestCase assert_equal %(

#{url[0...7]}...
#{email[0...7]}...

), auto_link("

#{url}
#{email}

") { |url| truncate(url, :length => 10) } end + + def test_auto_link_with_block_with_html + pic = "http://example.com/pic.png" + url = "http://example.com/album?a&b=c" + + assert_equal %(My pic: -- full album here #{generate_result(url)}), auto_link("My pic: #{pic} -- full album here #{url}") { |link| + if link =~ /\.(jpg|gif|png|bmp|tif)$/i + raw %() + else + link + end + } + end def test_auto_link_with_options_hash assert_dom_equal 'Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.', -- 1.6.4.2 From 9e08e196fa5c7183aec1f6dbeebe6bb7539e3c66 Mon Sep 17 00:00:00 2001 From: Lance Ivy Date: Wed, 15 Apr 2009 16:46:30 -0700 Subject: [PATCH 670/779] Ensure auto_link does not ignore multiple trailing punctuations [#2504 state:resolved] --- actionpack/lib/action_view/helpers/text_helper.rb | 13 +++++++------ actionpack/test/template/text_helper_test.rb | 7 +++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 6690e02..532ae0b 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -549,24 +549,25 @@ module ActionView link_attributes = html_options.stringify_keys text.gsub(AUTO_LINK_RE) do scheme, href = $1, $& - punctuation = '' + punctuation = [] if auto_linked?($`, $') # do not change string; URL is already linked 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 scheme - 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/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index d40d021..03c2611 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -452,6 +452,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") -- 1.6.4.2 From c66013e2c5dc77e9bfa06111fb8841b9b0e41ceb Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 24 May 2010 09:12:00 -0700 Subject: [PATCH 671/779] Fix that captured content (e.g. with form_for or div_for) would be HTML-escaped even without the rails_xss plugin installed. Rails 2.3.7, we barely knew ya... --- .../lib/action_view/helpers/capture_helper.rb | 2 +- actionpack/lib/action_view/test_case.rb | 2 +- actionpack/test/controller/capture_test.rb | 2 +- actionpack/test/fixtures/test/capturing.erb | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 80ccf37..bf26590 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -124,7 +124,7 @@ module ActionView # Use an alternate output buffer for the duration of the block. # Defaults to a new empty string. - def with_output_buffer(buf = ActiveSupport::SafeBuffer.new) #:nodoc: + def with_output_buffer(buf = '') #:nodoc: self.output_buffer, old_buffer = buf, output_buffer yield output_buffer diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 61749b2..049314c 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -53,7 +53,7 @@ module ActionView setup :setup_with_controller def setup_with_controller @controller = TestController.new - @output_buffer = ''.html_safe + @output_buffer = '' @rendered = '' self.class.send(:include_helper_modules!) diff --git a/actionpack/test/controller/capture_test.rb b/actionpack/test/controller/capture_test.rb index 6dfa099..56e3701 100644 --- a/actionpack/test/controller/capture_test.rb +++ b/actionpack/test/controller/capture_test.rb @@ -36,7 +36,7 @@ class CaptureTest < ActionController::TestCase def test_simple_capture get :capturing - assert_equal "Dreamy days", @response.body.strip + assert_equal "

Dreamy days

", @response.body.strip end def test_content_for diff --git a/actionpack/test/fixtures/test/capturing.erb b/actionpack/test/fixtures/test/capturing.erb index 1addaa4..cdca976 100644 --- a/actionpack/test/fixtures/test/capturing.erb +++ b/actionpack/test/fixtures/test/capturing.erb @@ -1,4 +1,4 @@ <% days = capture do %> - Dreamy days +

Dreamy days

<% end %> -<%= days %> \ No newline at end of file +<%= days %> -- 1.6.4.2 From 50b7c0c104a87a9b650f4d257d986823cc34cf13 Mon Sep 17 00:00:00 2001 From: wycats Date: Mon, 24 May 2010 11:18:20 -0700 Subject: [PATCH 672/779] Give the ERB String the encoding of the original template --- .../lib/action_view/template_handlers/erb.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb index 407de4b..164186f 100644 --- a/actionpack/lib/action_view/template_handlers/erb.rb +++ b/actionpack/lib/action_view/template_handlers/erb.rb @@ -12,7 +12,7 @@ module ActionView def compile(template) magic = $1 if template.source =~ /\A(<%#.*coding[:=]\s*(\S+)\s*-?%>)/ - erb = "#{magic}<% __in_erb_template=true %>#{template.source}" + erb = "#{magic}<% __in_erb_template=true %>#{template.source}".force_encoding(template.source.encoding) ::ERB.new(erb, nil, erb_trim_mode, '@output_buffer').src end end -- 1.6.4.2 From 8e6a044b2b8baad14044bc615a0662f3510e54d5 Mon Sep 17 00:00:00 2001 From: wycats Date: Mon, 24 May 2010 11:29:12 -0700 Subject: [PATCH 673/779] Needs to work on 1.8 too --- .../lib/action_view/template_handlers/erb.rb | 7 ++++++- 1 files changed, 6 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb index 164186f..b53a652 100644 --- a/actionpack/lib/action_view/template_handlers/erb.rb +++ b/actionpack/lib/action_view/template_handlers/erb.rb @@ -12,7 +12,12 @@ module ActionView def compile(template) magic = $1 if template.source =~ /\A(<%#.*coding[:=]\s*(\S+)\s*-?%>)/ - erb = "#{magic}<% __in_erb_template=true %>#{template.source}".force_encoding(template.source.encoding) + erb = "#{magic}<% __in_erb_template=true %>#{template.source}" + + if erb.respond_to?(:force_encoding) + erb.force_encoding(template.source.encoding) + end + ::ERB.new(erb, nil, erb_trim_mode, '@output_buffer').src end end -- 1.6.4.2 From d3da1a2c66ea68b88b6d99b252babf7ea2950aa1 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 24 May 2010 11:41:43 -0300 Subject: [PATCH 674/779] Revert "translation method of TranslationHelper module returns always SafeBuffer [#4194 status:resolved]" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2310aef29be306704c0361f1188200f71df657df. Signed-off-by: José Valim --- .../lib/action_view/helpers/translation_helper.rb | 2 +- .../test/fixtures/test/array_translation.erb | 1 - .../test/template/translation_helper_test.rb | 9 +-------- 3 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 actionpack/test/fixtures/test/array_translation.erb diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 9296622..684e250 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -13,7 +13,7 @@ module ActionView def translate(key, options = {}) options[:raise] = true translation = I18n.translate(scope_key_by_partial(key), options) - (translation.respond_to?(:join) ? translation.join : translation).html_safe + translation.respond_to?(:html_safe) ? translation.html_safe : translation rescue I18n::MissingTranslationData => e keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope]) content_tag('span', keys.join(', '), :class => 'translation_missing') diff --git a/actionpack/test/fixtures/test/array_translation.erb b/actionpack/test/fixtures/test/array_translation.erb deleted file mode 100644 index 12c0763..0000000 --- a/actionpack/test/fixtures/test/array_translation.erb +++ /dev/null @@ -1 +0,0 @@ -<%= t(['foo', 'bar']) %> \ No newline at end of file diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index 24640e4..e59697c 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -20,14 +20,7 @@ class TranslationHelperTest < Test::Unit::TestCase def test_translation_of_an_array I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"]) - assert_equal "foobar", translate(["foo", "bar"]) - end - - def test_translation_of_an_array_with_html - expected = 'foobar' - I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(['foo', 'bar']) - @view = ActionView::Base.new(ActionController::Base.view_paths, {}) - assert_equal expected, @view.render(:file => "test/array_translation") + assert_equal ["foo", "bar"], translate(["foo", "bar"]) end def test_delegates_localize_to_i18n -- 1.6.4.2 From 6b0616d1b8c8a3333020e34b4a160e4ec37a8889 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 24 May 2010 14:08:24 -0300 Subject: [PATCH 675/779] translation method of TranslationHelper module returns a SafeBuffer Array backport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4675 state:committed] Signed-off-by: José Valim --- .../lib/action_view/helpers/translation_helper.rb | 33 +++++++++----- .../test/fixtures/test/array_translation.erb | 1 + .../fixtures/test/scoped_array_translation.erb | 2 +- actionpack/test/fixtures/test/translation.erb | 1 + .../test/template/translation_helper_test.rb | 47 +++++++++++++++----- 5 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 actionpack/test/fixtures/test/array_translation.erb create mode 100644 actionpack/test/fixtures/test/translation.erb diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 684e250..1f95d02 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -3,17 +3,21 @@ require 'action_view/helpers/tag_helper' module ActionView module Helpers module TranslationHelper - # Delegates to I18n#translate but also performs two additional functions. First, it'll catch MissingTranslationData exceptions + # Delegates to I18n#translate but also performs two additional functions. First, it'll catch MissingTranslationData exceptions # and turn them into inline spans that contains the missing key, such that you can see in a view what is missing where. # # Second, it'll scope the key by the current partial if the key starts with a period. So if you call translate(".foo") from the # people/index.html.erb template, you'll actually be calling I18n.translate("people.index.foo"). This makes it less repetitive # to translate many keys within the same partials and gives you a simple framework for scoping them consistently. If you don't # prepend the key with a period, nothing is converted. - def translate(key, options = {}) - options[:raise] = true - translation = I18n.translate(scope_key_by_partial(key), options) - translation.respond_to?(:html_safe) ? translation.html_safe : translation + def translate(keys, options = {}) + options[:raise] = true + are_keys_a_string = keys.is_a?(String) + keys = scope_keys_by_partial(keys) + + translations = I18n.translate(keys, options) + translations = html_safe_translation_keys(keys, Array.wrap(translations)) + are_keys_a_string ? translations.first : translations rescue I18n::MissingTranslationData => e keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope]) content_tag('span', keys.join(', '), :class => 'translation_missing') @@ -28,12 +32,19 @@ module ActionView private - def scope_key_by_partial(key) - strkey = key.respond_to?(:join) ? key.join : key.to_s - if strkey.first == "." - template.path_without_format_and_extension.gsub(%r{/_?}, ".") + strkey - else - key + def scope_keys_by_partial(keys) + Array.wrap(keys).map do |key| + if key.to_s.first == "." + template.path_without_format_and_extension.gsub(%r{/_?}, ".") + key.to_s + else + key + end + end + end + + def html_safe_translation_keys(keys, translations) + keys.zip(translations).map do |key, translation| + key =~ /(\b|_|\.)html$/ ? translation.html_safe : translation end end end diff --git a/actionpack/test/fixtures/test/array_translation.erb b/actionpack/test/fixtures/test/array_translation.erb new file mode 100644 index 0000000..bcdeea4 --- /dev/null +++ b/actionpack/test/fixtures/test/array_translation.erb @@ -0,0 +1 @@ +<% translation = t(['foo', 'bar', 'baz_html']) %><%= translation.first %>, <%= translation.second %>, <%= translation.third %> \ No newline at end of file diff --git a/actionpack/test/fixtures/test/scoped_array_translation.erb b/actionpack/test/fixtures/test/scoped_array_translation.erb index 0a0c79f..cb07fca 100644 --- a/actionpack/test/fixtures/test/scoped_array_translation.erb +++ b/actionpack/test/fixtures/test/scoped_array_translation.erb @@ -1 +1 @@ -<%= t(['.foo', '.bar']) %> \ No newline at end of file +<%= t(['.foo', '.bar']).join(", ") %> \ No newline at end of file diff --git a/actionpack/test/fixtures/test/translation.erb b/actionpack/test/fixtures/test/translation.erb new file mode 100644 index 0000000..81a837d --- /dev/null +++ b/actionpack/test/fixtures/test/translation.erb @@ -0,0 +1 @@ +<%= t('.helper') %> \ No newline at end of file diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index e59697c..c6ab64d 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -1,43 +1,66 @@ require 'abstract_unit' -class TranslationHelperTest < Test::Unit::TestCase +class TranslationHelperTest < ActiveSupport::TestCase include ActionView::Helpers::TagHelper include ActionView::Helpers::TranslationHelper - + attr_reader :request def setup end - + def test_delegates_to_i18n_setting_the_raise_option - I18n.expects(:translate).with(:foo, :locale => 'en', :raise => true).returns("") + I18n.expects(:translate).with([:foo], :locale => 'en', :raise => true).returns([""]) translate :foo, :locale => 'en' end - + def test_returns_missing_translation_message_wrapped_into_span expected = 'en, foo' assert_equal expected, translate(:foo) end - + def test_translation_of_an_array I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"]) assert_equal ["foo", "bar"], translate(["foo", "bar"]) end + def test_translation_of_an_array_with_html + translate_expected = ['foo', 'bar', 'baz'] + I18n.expects(:translate).with(["foo", "bar", "baz_html"], :raise => true).returns(translate_expected) + @view = ActionView::Base.new(ActionController::Base.view_paths, {}) + expected = 'foo, bar, baz' + assert_equal expected, @view.render(:file => "test/array_translation") + end + def test_delegates_localize_to_i18n @time = Time.utc(2008, 7, 8, 12, 18, 38) I18n.expects(:localize).with(@time) localize @time end - + def test_scoping_by_partial - expects(:template).returns(stub(:path_without_format_and_extension => "people/index")) - I18n.expects(:translate).with("people.index.foo", :locale => 'en', :raise => true).returns("") - translate ".foo", :locale => 'en' + I18n.expects(:translate).with(["test.translation.helper"], :raise => true).returns(["helper"]) + @view = ActionView::Base.new(ActionController::Base.view_paths, {}) + assert_equal "helper", @view.render(:file => "test/translation") end def test_scoping_by_partial_of_an_array - I18n.expects(:translate).with("test.scoped_array_translation.foo.bar", :raise => true).returns(["foo", "bar"]) + I18n.expects(:translate).with(["test.scoped_array_translation.foo", "test.scoped_array_translation.bar"], :raise => true).returns(["foo", "bar"]) @view = ActionView::Base.new(ActionController::Base.view_paths, {}) - assert_equal "foobar", @view.render(:file => "test/scoped_array_translation") + assert_equal "foo, bar", @view.render(:file => "test/scoped_array_translation") + end + + def test_translate_does_not_mark_plain_text_as_safe_html + I18n.expects(:translate).with(["hello"], :raise => true).returns(["Hello World"]) + assert_equal false, translate("hello").html_safe? + end + + def test_translate_marks_translations_named_html_as_safe_html + I18n.expects(:translate).with(["html"], :raise => true).returns(["Hello World"]) + assert translate("html").html_safe? + end + + def test_translate_marks_translations_with_a_html_suffix_as_safe_html + I18n.expects(:translate).with(["hello_html"], :raise => true).returns(["Hello World"]) + assert translate("hello_html").html_safe? end end -- 1.6.4.2 From 9d3bd87045d49b5758d75cfee871bfd5289191de Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 24 May 2010 12:16:15 -0700 Subject: [PATCH 676/779] Work around strange Ruby 1.9 autoload issue by using absolute load paths for tests --- actionpack/test/abstract_unit.rb | 10 +++++----- 1 files changed, 5 insertions(+), 5 deletions(-) diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 3998862..096798d 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -1,7 +1,7 @@ -$:.unshift(File.dirname(__FILE__) + '/../lib') -$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib') -$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers') -$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers') +$:.unshift File.expand_path('../../lib', __FILE__) +$:.unshift File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift File.expand_path('../fixtures/helpers', __FILE__) +$:.unshift File.expand_path('../fixtures/alternate_helpers', __FILE__) require 'rubygems' require 'yaml' @@ -58,4 +58,4 @@ class DummyMutex end end -ActionController::Reloader.default_lock = DummyMutex.new \ No newline at end of file +ActionController::Reloader.default_lock = DummyMutex.new -- 1.6.4.2 From e5af56abfefe33cd5c4d0486ceaf6c1bf67cb490 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 24 May 2010 13:48:13 -0700 Subject: [PATCH 677/779] Add global gem task --- Rakefile | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/Rakefile b/Rakefile index efafd46..91d5196 100644 --- a/Rakefile +++ b/Rakefile @@ -12,7 +12,7 @@ end desc 'Run all tests by default' task :default => :test -%w(test rdoc pgem package release).each do |task_name| +%w(test rdoc pgem package release gem).each do |task_name| desc "Run #{task_name} task for all projects" task task_name do PROJECTS.each do |project| -- 1.6.4.2 From 4fef5af9c3632083285cbe52f9baacb64fe54ef6 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 24 May 2010 14:01:53 -0700 Subject: [PATCH 678/779] 2.3.7.pre1: fixes HTML escaping when *not* using rails_xss --- actionmailer/CHANGELOG | 4 ++-- actionpack/CHANGELOG | 4 ++-- activerecord/CHANGELOG | 4 ++-- activeresource/CHANGELOG | 4 ++-- activesupport/CHANGELOG | 4 ++-- railties/CHANGELOG | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 73f1aa5..4294a55 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.8 (unreleased)* +*2.3.8 (May 24, 2010)* -* ... +* Version bump. *2.3.7 (May 24, 2010)* diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 66d1093..1fa69e4 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.8 (unreleased)* +*2.3.8 (May 24, 2010)* -* ... +* HTML safety: fix compatibility *without* the optional rails_xss plugin. *2.3.7 (May 24, 2010)* diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 321b49c..84bd6ea 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.8 (unreleased)* +*2.3.8 (May 24, 2010)* -* ... +* Version bump. *2.3.7 (May 24, 2010)* diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index 738df13..130d927 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.8 (unreleased)* +*2.3.8 (May 24, 2010)* -* ... +* Version bump. *2.3.7 (May 24, 2010)* diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index abe6f56..e901382 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.8 (unreleased)* +*2.3.8 (May 24, 2010)* -* ... +* Version bump. *2.3.7 (May 24, 2010)* diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 2185ec6..674cfdc 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,6 +1,6 @@ -*2.3.8 (unreleased)* +*2.3.8 (May 24, 2010)* -* ... +* Version bump. *2.3.7 (May 24, 2010)* -- 1.6.4.2 From 4986d5ed04dba1480fe1f4a0368f099028404b20 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 24 May 2010 18:19:18 -0300 Subject: [PATCH 679/779] translate helper method using an array is deprecated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_view/helpers/translation_helper.rb | 3 ++ .../test/template/translation_helper_test.rb | 26 ++++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 1f95d02..eb05a15 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -11,6 +11,9 @@ module ActionView # to translate many keys within the same partials and gives you a simple framework for scoping them consistently. If you don't # prepend the key with a period, nothing is converted. def translate(keys, options = {}) + if keys.is_a?(Array) + ActiveSupport::Deprecation.warn "Giving an array to translate is deprecated, please give a symbol or a string instead", caller + end options[:raise] = true are_keys_a_string = keys.is_a?(String) keys = scope_keys_by_partial(keys) diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index c6ab64d..5d12bcc 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -19,16 +19,20 @@ class TranslationHelperTest < ActiveSupport::TestCase end def test_translation_of_an_array - I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"]) - assert_equal ["foo", "bar"], translate(["foo", "bar"]) + assert_deprecated do + I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"]) + assert_equal ["foo", "bar"], translate(["foo", "bar"]) + end end def test_translation_of_an_array_with_html - translate_expected = ['foo', 'bar', 'baz'] - I18n.expects(:translate).with(["foo", "bar", "baz_html"], :raise => true).returns(translate_expected) - @view = ActionView::Base.new(ActionController::Base.view_paths, {}) - expected = 'foo, bar, baz' - assert_equal expected, @view.render(:file => "test/array_translation") + assert_deprecated do + translate_expected = ['foo', 'bar', 'baz'] + I18n.expects(:translate).with(["foo", "bar", "baz_html"], :raise => true).returns(translate_expected) + @view = ActionView::Base.new(ActionController::Base.view_paths, {}) + expected = 'foo, bar, baz' + assert_equal expected, @view.render(:file => "test/array_translation") + end end def test_delegates_localize_to_i18n @@ -44,9 +48,11 @@ class TranslationHelperTest < ActiveSupport::TestCase end def test_scoping_by_partial_of_an_array - I18n.expects(:translate).with(["test.scoped_array_translation.foo", "test.scoped_array_translation.bar"], :raise => true).returns(["foo", "bar"]) - @view = ActionView::Base.new(ActionController::Base.view_paths, {}) - assert_equal "foo, bar", @view.render(:file => "test/scoped_array_translation") + assert_deprecated do + I18n.expects(:translate).with(["test.scoped_array_translation.foo", "test.scoped_array_translation.bar"], :raise => true).returns(["foo", "bar"]) + @view = ActionView::Base.new(ActionController::Base.view_paths, {}) + assert_equal "foo, bar", @view.render(:file => "test/scoped_array_translation") + end end def test_translate_does_not_mark_plain_text_as_safe_html -- 1.6.4.2 From 50f3754525c61e3ea84a407eb571617f2f39d6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 24 May 2010 23:36:43 +0200 Subject: [PATCH 680/779] Ensure translations work with symbols. --- .../lib/action_view/helpers/translation_helper.rb | 13 ++++++++----- .../test/template/translation_helper_test.rb | 8 +++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index eb05a15..60d0bb7 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -14,13 +14,14 @@ module ActionView if keys.is_a?(Array) ActiveSupport::Deprecation.warn "Giving an array to translate is deprecated, please give a symbol or a string instead", caller end - options[:raise] = true - are_keys_a_string = keys.is_a?(String) + + options[:raise] = true + return_first = keys.is_a?(String) || keys.is_a?(Symbol) keys = scope_keys_by_partial(keys) translations = I18n.translate(keys, options) translations = html_safe_translation_keys(keys, Array.wrap(translations)) - are_keys_a_string ? translations.first : translations + return_first ? translations.first : translations rescue I18n::MissingTranslationData => e keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope]) content_tag('span', keys.join(', '), :class => 'translation_missing') @@ -37,8 +38,10 @@ module ActionView private def scope_keys_by_partial(keys) Array.wrap(keys).map do |key| - if key.to_s.first == "." - template.path_without_format_and_extension.gsub(%r{/_?}, ".") + key.to_s + key = key.to_s + + if key.first == "." + template.path_without_format_and_extension.gsub(%r{/_?}, ".") + key else key end diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index 5d12bcc..4887c66 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -9,7 +9,7 @@ class TranslationHelperTest < ActiveSupport::TestCase end def test_delegates_to_i18n_setting_the_raise_option - I18n.expects(:translate).with([:foo], :locale => 'en', :raise => true).returns([""]) + I18n.expects(:translate).with(['foo'], :locale => 'en', :raise => true).returns([""]) translate :foo, :locale => 'en' end @@ -55,6 +55,12 @@ class TranslationHelperTest < ActiveSupport::TestCase end end + def test_translate_works_with_symbols + I18n.expects(:translate).with(["hello"], :raise => true).returns(["Hello World"]) + assert_equal "Hello World", translate(:hello) + end + + def test_translate_does_not_mark_plain_text_as_safe_html I18n.expects(:translate).with(["hello"], :raise => true).returns(["Hello World"]) assert_equal false, translate("hello").html_safe? -- 1.6.4.2 From e8ba5265e0ce0aa25925ae0542ac4455f9f61719 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 24 May 2010 16:02:34 -0700 Subject: [PATCH 681/779] Work around strange Ruby 1.9 autoload issue by using absolute load paths for tests (ditto for other components' tests) --- actionmailer/test/abstract_unit.rb | 6 +++--- actionpack/test/active_record_unit.rb | 2 +- activeresource/test/abstract_unit.rb | 4 ++-- activesupport/test/abstract_unit.rb | 2 +- railties/test/abstract_unit.rb | 12 ++++++------ 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 0aed45b..2cb862a 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -1,9 +1,9 @@ require 'rubygems' require 'test/unit' -$:.unshift "#{File.dirname(__FILE__)}/../lib" -$:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift "#{File.dirname(__FILE__)}/../../actionpack/lib" +$:.unshift File.expand_path('../../lib', __FILE__) +$:.unshift File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift File.expand_path('../../../actionpack/lib', __FILE__) require 'action_mailer' require 'action_mailer/test_case' diff --git a/actionpack/test/active_record_unit.rb b/actionpack/test/active_record_unit.rb index 9e0c660..5c0cb06 100644 --- a/actionpack/test/active_record_unit.rb +++ b/actionpack/test/active_record_unit.rb @@ -16,7 +16,7 @@ if defined?(ActiveRecord) && defined?(Fixtures) else $stderr.print 'Attempting to load Active Record... ' begin - PATH_TO_AR = "#{File.dirname(__FILE__)}/../../activerecord/lib" + PATH_TO_AR = File.expand_path('../../../activerecord/lib', __FILE__) raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR) $LOAD_PATH.unshift PATH_TO_AR require 'active_record' diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb index 07d201a..c769bf0 100644 --- a/activeresource/test/abstract_unit.rb +++ b/activeresource/test/abstract_unit.rb @@ -2,8 +2,8 @@ require 'rubygems' require 'test/unit' require 'active_support/test_case' -$:.unshift "#{File.dirname(__FILE__)}/../lib" -$:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib" +$:.unshift File.expand_path('../../lib', __FILE__) +$:.unshift File.expand_path('../../../activesupport/lib', __FILE__) require 'active_resource' require 'active_resource/http_mock' diff --git a/activesupport/test/abstract_unit.rb b/activesupport/test/abstract_unit.rb index c2ed015..26f11b3 100644 --- a/activesupport/test/abstract_unit.rb +++ b/activesupport/test/abstract_unit.rb @@ -4,7 +4,7 @@ require 'empty_bool' ENV['NO_RELOAD'] = '1' -$:.unshift "#{File.dirname(__FILE__)}/../lib" +$:.unshift File.expand_path('../../lib', __FILE__) require 'active_support' require 'active_support/test_case' diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb index 85f680d..60f2c91 100644 --- a/railties/test/abstract_unit.rb +++ b/railties/test/abstract_unit.rb @@ -1,9 +1,9 @@ -$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" -$:.unshift File.dirname(__FILE__) + "/../../activerecord/lib" -$:.unshift File.dirname(__FILE__) + "/../../actionpack/lib" -$:.unshift File.dirname(__FILE__) + "/../../actionmailer/lib" -$:.unshift File.dirname(__FILE__) + "/../lib" -$:.unshift File.dirname(__FILE__) + "/../builtin/rails_info" +$:.unshift File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift File.expand_path('../../../activerecord/lib', __FILE__) +$:.unshift File.expand_path('../../../actionpack/lib', __FILE__) +$:.unshift File.expand_path('../../../actionmailer/lib', __FILE__) +$:.unshift File.expand_path('../../lib', __FILE__) +$:.unshift File.expand_path('../../builtin/rails_info', __FILE__) require 'stringio' require 'rubygems' -- 1.6.4.2 From a9032c885f1ff773b193be5bd902cb482ce0f626 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 24 May 2010 17:16:16 -0300 Subject: [PATCH 682/779] Error messages for asserts Signed-off-by: Jeremy Kemper --- actionpack/test/controller/assert_select_test.rb | 10 +++++----- actionpack/test/controller/helper_test.rb | 2 +- activesupport/test/buffered_logger_test.rb | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb index 1306541..58fcaa9 100644 --- a/actionpack/test/controller/assert_select_test.rb +++ b/actionpack/test/controller/assert_select_test.rb @@ -210,13 +210,13 @@ class AssertSelectTest < ActionController::TestCase assert_nothing_raised { assert_select "div", "foo" } assert_nothing_raised { assert_select "div", "bar" } assert_nothing_raised { assert_select "div", /\w*/ } - assert_nothing_raised { assert_select "div", /\w*/, :count=>2 } - assert_raise(Assertion) { assert_select "div", :text=>"foo", :count=>2 } + assert_nothing_raised { assert_select "div", :text => /\w*/, :count=>2 } + assert_raise(Assertion) { assert_select "div", :text=>"foo", :count=>2 } assert_nothing_raised { assert_select "div", :html=>"bar" } assert_nothing_raised { assert_select "div", :html=>"bar" } assert_nothing_raised { assert_select "div", :html=>/\w*/ } assert_nothing_raised { assert_select "div", :html=>/\w*/, :count=>2 } - assert_raise(Assertion) { assert_select "div", :html=>"foo", :count=>2 } + assert_raise(Assertion) { assert_select "div", :html=>"foo", :count=>2 } end end @@ -268,8 +268,8 @@ class AssertSelectTest < ActionController::TestCase def test_css_select render_html %Q{
} - assert 2, css_select("div").size - assert 0, css_select("p").size + assert_equal 2, css_select("div").size + assert_equal 0, css_select("p").size end def test_nested_css_select diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 5f36461..4914586 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -87,7 +87,7 @@ class HelperTest < Test::Unit::TestCase assert_nothing_raised { @controller_class.helper { include HelperTest::TestHelper } } - assert [], missing_methods + assert_equal [], missing_methods end def test_helper_method diff --git a/activesupport/test/buffered_logger_test.rb b/activesupport/test/buffered_logger_test.rb index e178ced..380c98f 100644 --- a/activesupport/test/buffered_logger_test.rb +++ b/activesupport/test/buffered_logger_test.rb @@ -66,11 +66,11 @@ class BufferedLoggerTest < Test::Unit::TestCase 4.times do @logger.info 'wait for it..' - assert @output.string.empty?, @output.string + assert @output.string.empty?, "@output.string should be empty but it is #{@output.string}" end @logger.flush - assert !@output.string.empty?, @logger.send(:buffer).size + assert !@output.string.empty?, "@logger.send(:buffer).size.to_s should not be empty but it is empty" end define_method "test_disabling_auto_flush_with_#{disable.inspect}_should_flush_at_max_buffer_size_as_failsafe" do @@ -79,11 +79,11 @@ class BufferedLoggerTest < Test::Unit::TestCase (ActiveSupport::BufferedLogger::MAX_BUFFER_SIZE - 1).times do @logger.info 'wait for it..' - assert @output.string.empty?, @output.string + assert @output.string.empty?, "@output.string should be empty but is #{@output.string}" end @logger.info 'there it is.' - assert !@output.string.empty?, @logger.send(:buffer).size + assert !@output.string.empty?, "@logger.send(:buffer).size.to_s should not be empty but it is empty" end end @@ -99,11 +99,11 @@ class BufferedLoggerTest < Test::Unit::TestCase 4.times do @logger.info 'wait for it..' - assert @output.string.empty?, @output.string + assert @output.string.empty?, "@output.string should be empty but it is #{@output.string}" end @logger.info 'there it is.' - assert !@output.string.empty?, @output.string + assert !@output.string.empty?, "@output.string should not be empty but it is empty" end def test_should_create_the_log_directory_if_it_doesnt_exist -- 1.6.4.2 From aa449141b427bc5cc32650d6f5c9813e71b7d237 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 24 May 2010 16:18:29 -0700 Subject: [PATCH 683/779] Work around strange Ruby 1.9 autoload issue by using absolute load paths for tests (for Active Model too) --- activemodel/test/test_helper.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/activemodel/test/test_helper.rb b/activemodel/test/test_helper.rb index 5b5678e..c4c1d11 100644 --- a/activemodel/test/test_helper.rb +++ b/activemodel/test/test_helper.rb @@ -7,7 +7,7 @@ require 'mocha' require 'active_model' require 'active_model/state_machine' -$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib" +$:.unshift File.expand_path('../../../activesupport/lib', __FILE__) require 'active_support' require 'active_support/test_case' -- 1.6.4.2 From 6a9e188c0c925634749478eb7cde43aead9b133f Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 24 May 2010 20:13:07 -0700 Subject: [PATCH 684/779] HTML safety: fix textarea with nil content --- .../lib/action_view/helpers/form_tag_helper.rb | 2 +- actionpack/test/template/form_tag_helper_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 37c9dcd..ac62ba5 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -265,7 +265,7 @@ module ActionView escape = options.key?("escape") ? options.delete("escape") : true content = html_escape(content) if escape - content_tag :textarea, content.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) + content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys) end # Creates a check box form input tag. diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 4606047..95778ac 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -176,6 +176,12 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal expected, actual end + def test_text_area_tag_unescaped_nil_content + actual = text_area_tag "body", nil, :escape => false + expected = %() + assert_dom_equal expected, actual + end + def test_text_field_tag actual = text_field_tag "title", "Hello!" expected = %() -- 1.6.4.2 From f7e27bd078c9fa25c2786faf1c499c79155bbb3c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 24 May 2010 20:16:50 -0700 Subject: [PATCH 685/779] i18n: t() handles single keys returning an Array, also --- .../lib/action_view/helpers/translation_helper.rb | 19 ++++++++++++++----- .../test/template/translation_helper_test.rb | 17 +++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 60d0bb7..f700eee 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -11,17 +11,22 @@ module ActionView # to translate many keys within the same partials and gives you a simple framework for scoping them consistently. If you don't # prepend the key with a period, nothing is converted. def translate(keys, options = {}) - if keys.is_a?(Array) + if multiple_keys = keys.is_a?(Array) ActiveSupport::Deprecation.warn "Giving an array to translate is deprecated, please give a symbol or a string instead", caller end options[:raise] = true - return_first = keys.is_a?(String) || keys.is_a?(Symbol) keys = scope_keys_by_partial(keys) translations = I18n.translate(keys, options) - translations = html_safe_translation_keys(keys, Array.wrap(translations)) - return_first ? translations.first : translations + translations = [translations] if !multiple_keys && translations.size > 1 + translations = html_safe_translation_keys(keys, translations) + + if multiple_keys || translations.size > 1 + translations + else + translations.first + end rescue I18n::MissingTranslationData => e keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope]) content_tag('span', keys.join(', '), :class => 'translation_missing') @@ -50,7 +55,11 @@ module ActionView def html_safe_translation_keys(keys, translations) keys.zip(translations).map do |key, translation| - key =~ /(\b|_|\.)html$/ ? translation.html_safe : translation + if key =~ /(\b|_|\.)html$/ && translation.respond_to?(:html_safe) + translation.html_safe + else + translation + end end end end diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index 4887c66..6555eaa 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -18,6 +18,11 @@ class TranslationHelperTest < ActiveSupport::TestCase assert_equal expected, translate(:foo) end + def test_translation_returning_an_array + I18n.expects(:translate).with(["foo"], :raise => true).returns(["foo", "bar"]) + assert_equal ["foo", "bar"], translate(:foo) + end + def test_translation_of_an_array assert_deprecated do I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", "bar"]) @@ -25,6 +30,13 @@ class TranslationHelperTest < ActiveSupport::TestCase end end + def test_translation_of_an_array_returning_an_array + assert_deprecated do + I18n.expects(:translate).with(["foo", "bar"], :raise => true).returns(["foo", ["bar", "baz"]]) + assert_equal ["foo", ["bar", "baz"]], translate(["foo", "bar"]) + end + end + def test_translation_of_an_array_with_html assert_deprecated do translate_expected = ['foo', 'bar', 'baz'] @@ -75,4 +87,9 @@ class TranslationHelperTest < ActiveSupport::TestCase I18n.expects(:translate).with(["hello_html"], :raise => true).returns(["Hello World"]) assert translate("hello_html").html_safe? end + + def test_translation_returning_an_array_ignores_html_suffix + I18n.expects(:translate).with(["foo_html"], :raise => true).returns(["foo", "bar"]) + assert_equal ["foo", "bar"], translate(:foo_html) + end end -- 1.6.4.2 From 240f4e944cd90fca138aba8467456043952110cc Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sat, 22 May 2010 16:36:10 -0300 Subject: [PATCH 686/779] SQLite: forward compatibility with future driver releases [#4633] Signed-off-by: Jeremy Kemper --- .../connection_adapters/sqlite_adapter.rb | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index f82e5bf..5333999 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -220,20 +220,20 @@ module ActiveRecord SQL execute(sql, name).map do |row| - row[0] + row['name'] end end def columns(table_name, name = nil) #:nodoc: table_structure(table_name).map do |field| - SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'] == "0") + SQLiteColumn.new(field['name'], field['dflt_value'], field['type'], field['notnull'].to_i == 0) end end def indexes(table_name, name = nil) #:nodoc: execute("PRAGMA index_list(#{quote_table_name(table_name)})", name).map do |row| index = IndexDefinition.new(table_name, row['name']) - index.unique = row['unique'] != '0' + index.unique = row['unique'].to_i != 0 index.columns = execute("PRAGMA index_info('#{index.name}')").map { |col| col['name'] } index end -- 1.6.4.2 From 2ed893bdced9534d200dc2e916641ce6788a3b9c Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 25 May 2010 17:18:28 +0200 Subject: [PATCH 687/779] get railties/README back to the home page of the API --- Rakefile | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/Rakefile b/Rakefile index 91d5196..d108ee9 100644 --- a/Rakefile +++ b/Rakefile @@ -30,6 +30,7 @@ Rake::RDocTask.new do |rdoc| rdoc.options << '--line-numbers' << '--inline-source' rdoc.options << '-A cattr_accessor=object' rdoc.options << '--charset' << 'utf-8' + rdoc.options << '--main' << 'railties/README' rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : './doc/template/horo' -- 1.6.4.2 From 9da7ff8842e5e6407872153b20586deaaad5be01 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 25 May 2010 09:50:34 -0700 Subject: [PATCH 688/779] Bump 2-3-stable to 2.3.9 --- actionmailer/CHANGELOG | 5 +++++ actionmailer/Rakefile | 2 +- actionmailer/lib/action_mailer/version.rb | 2 +- actionpack/CHANGELOG | 5 +++++ actionpack/Rakefile | 2 +- actionpack/lib/action_pack/version.rb | 2 +- activerecord/CHANGELOG | 5 +++++ activerecord/Rakefile | 2 +- activerecord/lib/active_record/version.rb | 2 +- activeresource/CHANGELOG | 5 +++++ activeresource/Rakefile | 2 +- activeresource/lib/active_resource/version.rb | 2 +- activesupport/CHANGELOG | 5 +++++ activesupport/lib/active_support/version.rb | 2 +- railties/CHANGELOG | 5 +++++ railties/Rakefile | 10 +++++----- railties/lib/rails/version.rb | 2 +- 17 files changed, 45 insertions(+), 15 deletions(-) diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 4294a55..5323c9d 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.9 (unreleased)* + +* ... + + *2.3.8 (May 24, 2010)* * Version bump. diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index 4d1807a..afeab4a 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -54,7 +54,7 @@ spec = Gem::Specification.new do |s| s.rubyforge_project = "actionmailer" s.homepage = "http://www.rubyonrails.org" - s.add_dependency('actionpack', '= 2.3.8' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.9' + PKG_BUILD) s.has_rdoc = true s.requirements << 'none' diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index 6ad7a26..611a48b 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -2,7 +2,7 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 8 + TINY = 9 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 1fa69e4..05083b4 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.9 (unreleased)* + +* ... + + *2.3.8 (May 24, 2010)* * HTML safety: fix compatibility *without* the optional rails_xss plugin. diff --git a/actionpack/Rakefile b/actionpack/Rakefile index c0ebd0a..2a2ecd6 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -79,7 +79,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.3.8' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.9' + PKG_BUILD) s.add_dependency('rack', '~> 1.1.0') s.require_path = 'lib' diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index a464dda..3064148 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -2,7 +2,7 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 8 + TINY = 9 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 84bd6ea..42df7f9 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.9 (unreleased)* + +* ... + + *2.3.8 (May 24, 2010)* * Version bump. diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 6dd6989..1b2c37b 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -192,7 +192,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.8' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.9' + PKG_BUILD) s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite" s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite" diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index d6a7f60..4184884 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,7 +2,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 8 + TINY = 9 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index 130d927..d219f7d 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.9 (unreleased)* + +* ... + + *2.3.8 (May 24, 2010)* * Version bump. diff --git a/activeresource/Rakefile b/activeresource/Rakefile index fa11d0c..c47fb56 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -66,7 +66,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.3.8' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.9' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'active_resource' diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index 95b3cf9..4ea6d58 100644 --- a/activeresource/lib/active_resource/version.rb +++ b/activeresource/lib/active_resource/version.rb @@ -2,7 +2,7 @@ module ActiveResource module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 8 + TINY = 9 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index e901382..978f9f5 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.9 (unreleased)* + +* ... + + *2.3.8 (May 24, 2010)* * Version bump. diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index ac357e6..571f467 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -2,7 +2,7 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 8 + TINY = 9 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 674cfdc..50130ed 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,3 +1,8 @@ +*2.3.9 (unreleased)* + +* ... + + *2.3.8 (May 24, 2010)* * Version bump. diff --git a/railties/Rakefile b/railties/Rakefile index 350cfb5..1c34ba0 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -314,11 +314,11 @@ spec = Gem::Specification.new do |s| EOF s.add_dependency('rake', '>= 0.8.3') - s.add_dependency('activesupport', '= 2.3.8' + PKG_BUILD) - s.add_dependency('activerecord', '= 2.3.8' + PKG_BUILD) - s.add_dependency('actionpack', '= 2.3.8' + PKG_BUILD) - s.add_dependency('actionmailer', '= 2.3.8' + PKG_BUILD) - s.add_dependency('activeresource', '= 2.3.8' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.3.9' + PKG_BUILD) + s.add_dependency('activerecord', '= 2.3.9' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.3.9' + PKG_BUILD) + s.add_dependency('actionmailer', '= 2.3.9' + PKG_BUILD) + s.add_dependency('activeresource', '= 2.3.9' + PKG_BUILD) s.rdoc_options << '--exclude' << '.' s.has_rdoc = false diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index db11083..495bba2 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -2,7 +2,7 @@ module Rails module VERSION #:nodoc: MAJOR = 2 MINOR = 3 - TINY = 8 + TINY = 9 STRING = [MAJOR, MINOR, TINY].join('.') end -- 1.6.4.2 From a815f0c5a3a873aefca76f459ce05ddde73080db Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 25 May 2010 14:20:39 -0700 Subject: [PATCH 689/779] Shift SafeBuffer#concat responsibility over to rails_xss --- .../core_ext/string/output_safety.rb | 9 ------ activesupport/test/core_ext/string_ext_test.rb | 27 +++++-------------- activesupport/test/safe_buffer_test.rb | 4 +- 3 files changed, 9 insertions(+), 31 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index af26d99..6bdf0c6 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -64,15 +64,6 @@ module ActiveSupport #:nodoc: class SafeBuffer < String alias safe_concat concat - def concat(value) - if value.html_safe? - super(value) - else - super(ERB::Util.h(value)) - end - end - alias << concat - def +(other) dup.concat(other) end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 27c2c3b..ea24299 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -287,11 +287,6 @@ end class OutputSafetyTest < ActiveSupport::TestCase def setup @string = "hello" - @object = Class.new(Object) do - def to_s - "other" - end - end.new end test "A string is unsafe by default" do @@ -316,15 +311,7 @@ class OutputSafetyTest < ActiveSupport::TestCase end test "An object is unsafe by default" do - assert !@object.html_safe? - end - - test "Adding an object to a safe string returns a safe string" do - string = @string.html_safe - string << @object - - assert_equal "helloother", string - assert string.html_safe? + assert !Object.new.html_safe? end test "Adding a safe string to another safe string returns a safe string" do @@ -336,12 +323,12 @@ class OutputSafetyTest < ActiveSupport::TestCase assert @combination.html_safe? end - test "Adding an unsafe string to a safe string escapes it and returns a safe string" do + test "Adding an unsafe string to a safe string doesn't escape it without rails_xss but returns a safe string" do @other_string = "other".html_safe @combination = @other_string + "" @other_combination = @string + "" - assert_equal "other<foo>", @combination + assert_equal "other", @combination assert_equal "hello", @other_combination assert @combination.html_safe? @@ -356,10 +343,10 @@ class OutputSafetyTest < ActiveSupport::TestCase assert !@other_string.html_safe? end - test "Concatting unsafe onto safe yields escaped safe" do + test "Concatting unsafe onto safe yields safe by not escaped without rails_xss" do @other_string = "other".html_safe string = @other_string.concat("") - assert_equal "other<foo>", string + assert_equal "other", string assert string.html_safe? end @@ -379,10 +366,10 @@ class OutputSafetyTest < ActiveSupport::TestCase assert !@other_string.html_safe? end - test "Concatting unsafe onto safe with << yields escaped safe" do + test "Concatting unsafe onto safe with << yields safe but not escaped without rails_xss" do @other_string = "other".html_safe string = @other_string << "" - assert_equal "other<foo>", string + assert_equal "other", string assert string.html_safe? end diff --git a/activesupport/test/safe_buffer_test.rb b/activesupport/test/safe_buffer_test.rb index ea95e7e..7684d77 100644 --- a/activesupport/test/safe_buffer_test.rb +++ b/activesupport/test/safe_buffer_test.rb @@ -10,9 +10,9 @@ class SafeBufferTest < ActiveSupport::TestCase assert_equal "", @buffer end - test "Should escape a raw string which is passed to them" do + test "Should not escape a raw string unless using rails_xss" do @buffer << "