From c91912700d31f1f3b54192beefc6f5496804a396 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 24 Mar 2009 10:34:30 -0500 Subject: [PATCH 001/171] 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 From daffa5cbddace5a68ce09808a827805ef81ddb84 Mon Sep 17 00:00:00 2001 From: Peter Marklund Date: Tue, 24 Mar 2009 10:37:57 -0500 Subject: [PATCH 002/171] 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 From dace54b2e9bcdb49ffa43b204c3a8959c55f1276 Mon Sep 17 00:00:00 2001 From: David Dollar Date: Tue, 24 Mar 2009 10:41:45 -0500 Subject: [PATCH 003/171] 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 From d2e6a0fbc30d3fd03e7945306d18ed8ced8b20cf Mon Sep 17 00:00:00 2001 From: thedarkone Date: Tue, 24 Mar 2009 10:44:54 -0500 Subject: [PATCH 004/171] 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 From 6a1267a0b12560a752ebfb443194de1a0ab3bef5 Mon Sep 17 00:00:00 2001 From: thedarkone Date: Tue, 24 Mar 2009 10:48:47 -0500 Subject: [PATCH 005/171] 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 From 651611999df3e57de6f36486b51abd3bf5d66cea Mon Sep 17 00:00:00 2001 From: Ryan Angilly Date: Tue, 24 Mar 2009 10:51:45 -0500 Subject: [PATCH 006/171] 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 From ace154d067f619aca8a56988efdf9477898242ae Mon Sep 17 00:00:00 2001 From: Kenny Ortmann Date: Tue, 7 Apr 2009 09:18:42 -0500 Subject: [PATCH 007/171] 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 From dc69d9308aefb249eae3776e186f4c94fdf50eb7 Mon Sep 17 00:00:00 2001 From: Doug McInnes Date: Tue, 3 Feb 2009 18:37:55 -0800 Subject: [PATCH 008/171] 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 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/171] 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 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/171] 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 From c9a3d991640539375ba2bf8b6a0669a6c120df6d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Fri, 17 Apr 2009 13:44:59 -0500 Subject: [PATCH 011/171] 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 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/171] 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 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/171] 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 From 375e8976e34b11ee25e75c30959d7c3fc3be6866 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 20 Apr 2009 13:51:11 +0100 Subject: [PATCH 014/171] 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 From fc2421b78402c430f9037c4db81779129e3c43c8 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 20 Apr 2009 18:12:40 +0100 Subject: [PATCH 015/171] 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 From 60601234708b34c81dcd6a58a0cad79a7520ce10 Mon Sep 17 00:00:00 2001 From: Max Lapshin Date: Sat, 4 Apr 2009 18:11:33 +0100 Subject: [PATCH 016/171] 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 From de4cc53f7428090600b9e31ee39cf8c4c0df811e Mon Sep 17 00:00:00 2001 From: Max Lapshin Date: Mon, 20 Apr 2009 20:08:51 +0400 Subject: [PATCH 017/171] 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 From 70ba90b072025b89248606178ee30d2ff12301c4 Mon Sep 17 00:00:00 2001 From: Scott Woods Date: Mon, 20 Apr 2009 23:48:02 +0300 Subject: [PATCH 018/171] 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 From 5a4603fafbf4d959ebc8f9435457eaf6fa4004e9 Mon Sep 17 00:00:00 2001 From: Max Lapshin Date: Mon, 20 Apr 2009 19:47:31 +0400 Subject: [PATCH 019/171] 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 From 326709739361e1087131da0fa9b6cbd4bce5be8e Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 21 Apr 2009 13:06:26 +0100 Subject: [PATCH 020/171] 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 From 5bbd097ce990d6830c832435867b06108de5bf08 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 21 Apr 2009 13:11:56 +0100 Subject: [PATCH 021/171] 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 From 09a976ac58d2d7637003b92d51637f59f647b53a Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 21 Apr 2009 13:28:49 +0100 Subject: [PATCH 022/171] 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 From 2d9b45722c5639ad822f9a213edfd4d2bb084dcb Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 25 Apr 2009 13:44:34 -0500 Subject: [PATCH 023/171] 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 From 16f36b6171c855dcc92ef557c454fcfc314b617f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 25 Apr 2009 13:59:26 -0500 Subject: [PATCH 024/171] 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 From 61a14569379974564a98b229ab595dfec18d2059 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 25 Apr 2009 14:05:58 -0500 Subject: [PATCH 025/171] 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 From cb9a1f17f05908768a380dce01679adb8910e638 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sat, 25 Apr 2009 12:47:51 -0700 Subject: [PATCH 026/171] 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 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/171] 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 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/171] 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 From 4b68debb1c4d3d272b237049c88d01b1eceb58f0 Mon Sep 17 00:00:00 2001 From: Stephen Bannasch Date: Sun, 15 Mar 2009 06:32:29 -0400 Subject: [PATCH 029/171] 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 From 6e3bede928d95cbf5fe70ab2283adce28da3401d Mon Sep 17 00:00:00 2001 From: David Dollar Date: Wed, 29 Apr 2009 01:22:54 -0400 Subject: [PATCH 030/171] 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 From 00eee49e1e5d1ce394c639fc5542f993bae49ed7 Mon Sep 17 00:00:00 2001 From: David Dollar Date: Wed, 29 Apr 2009 10:04:17 -0400 Subject: [PATCH 031/171] 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 From 7c4b325e0a3a73d7910b8cad062d1154d47e225b Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 30 Apr 2009 16:34:00 -0700 Subject: [PATCH 032/171] 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 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/171] 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 From a5ed7eede609790fba9ae11db1e2fd46b9806aa3 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 30 Apr 2009 16:49:34 -0700 Subject: [PATCH 034/171] 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 From 628b4ad679b1971427a20461e8c2332d492e4655 Mon Sep 17 00:00:00 2001 From: Alexander Podgorbunsky Date: Thu, 26 Mar 2009 15:00:12 +0300 Subject: [PATCH 035/171] 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 From 93c557828e1873004911acfd25d3b3903210bc40 Mon Sep 17 00:00:00 2001 From: Ruy Asan Date: Fri, 1 May 2009 13:09:29 -0700 Subject: [PATCH 036/171] 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 From 17e712d3a3d3934bb1f694d449d9a76a3ac715c1 Mon Sep 17 00:00:00 2001 From: Ruy Asan Date: Fri, 1 May 2009 13:49:18 -0700 Subject: [PATCH 037/171] 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 From ebe8dd6108ab9b2ecd74bacf7fa128e9d4f28e4e Mon Sep 17 00:00:00 2001 From: codebrulee Date: Mon, 4 May 2009 09:51:35 -0700 Subject: [PATCH 038/171] 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 From 49169f7a6ab2699c500032e6e14570512c674274 Mon Sep 17 00:00:00 2001 From: Tim Connor Date: Mon, 4 May 2009 20:12:16 -0500 Subject: [PATCH 039/171] 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 From 5ac05f15c6d8f496c4e152dbbecd8ccb12041770 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Mon, 4 May 2009 20:17:27 -0500 Subject: [PATCH 040/171] 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 From 7f1f16c01fd42701747daf9f3399dcdd300125f4 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 4 May 2009 20:24:49 -0500 Subject: [PATCH 041/171] 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 From e61cceb37fa99a0eaf510e6e8138eca4b9cefcfd Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 8 May 2009 17:00:16 -0500 Subject: [PATCH 042/171] 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 From 6dec3c45fcf9afbd3da30afb354433fcc54155d6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 9 May 2009 18:35:31 -0400 Subject: [PATCH 043/171] 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 From 7bf9bf3dd6b4c4d78214917f0877536d222098bb Mon Sep 17 00:00:00 2001 From: John Small Date: Fri, 8 May 2009 06:47:04 +0100 Subject: [PATCH 044/171] 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 From 88d5e3341d0a602135c40d01ca0e5d8a345a0781 Mon Sep 17 00:00:00 2001 From: Anthony Crumley Date: Mon, 4 May 2009 09:49:43 -0500 Subject: [PATCH 045/171] 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 From 4051dd3412ad6b233b8bcdea17cfb829f21b2fa8 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 9 May 2009 22:21:02 -0500 Subject: [PATCH 046/171] 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 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/171] 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 From 35e17850819d99d78a3bd02865652c7882201bf0 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 11 May 2009 12:01:27 -0700 Subject: [PATCH 048/171] 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 From 0380e9ca5fa872e56d0920e6255a2f20b6e01030 Mon Sep 17 00:00:00 2001 From: Peter Marklund Date: Thu, 14 May 2009 09:30:16 +0200 Subject: [PATCH 049/171] 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 From f7cb7fce4cb25e608f0c2d94a4970c0c7cb7d3da Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 14 May 2009 16:47:24 -0500 Subject: [PATCH 050/171] 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 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/171] 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 From ba92e83bcc8120eeac9f9f53b1705f48360ec070 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 May 2009 17:04:43 +0200 Subject: [PATCH 052/171] 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 From 4cd40726eb10fbab269be53c27b304c728541dff Mon Sep 17 00:00:00 2001 From: Daniel Guettler Date: Sun, 17 May 2009 14:48:20 +0200 Subject: [PATCH 053/171] 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 From d5f018eb103b63738f7f3b2374242fbdd40cfd63 Mon Sep 17 00:00:00 2001 From: Jacob Kjeldahl Date: Tue, 27 Jan 2009 15:00:18 +0100 Subject: [PATCH 054/171] 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 From 14b769899cd045ba066e91d372906fe51eca319e Mon Sep 17 00:00:00 2001 From: Paulo Schneider Date: Mon, 6 Apr 2009 21:38:24 +0100 Subject: [PATCH 055/171] 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 From f383a4aa333cd8a99003eb1bdbb27b6fdea1056c Mon Sep 17 00:00:00 2001 From: Mike Breen Date: Mon, 16 Mar 2009 07:11:16 -0400 Subject: [PATCH 056/171] 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 From e30016c29e4ec58352a15511afa12f0fbbd03a8c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 17 May 2009 14:44:19 -0500 Subject: [PATCH 057/171] 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 From 43e537b9e8a8e34aaf57cec33cd91574ee5c1459 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 17 May 2009 14:45:06 -0500 Subject: [PATCH 058/171] 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 From d052e9fb5868a10df33a84bf61f40a32df9e78ec Mon Sep 17 00:00:00 2001 From: rick Date: Sun, 17 May 2009 14:55:11 -0700 Subject: [PATCH 059/171] 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 From cc47d3ff0c5e9d20b2590354a9992fbb0ea80e10 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 26 Apr 2009 15:18:33 -0700 Subject: [PATCH 060/171] 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 From 5b80ead2a3aafb3aea904b7e0f2115e1a56dc843 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 26 Apr 2009 15:53:32 -0700 Subject: [PATCH 061/171] 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 From 2b5e4f38f5d7af912770f8a298ba2e5788c715b1 Mon Sep 17 00:00:00 2001 From: rick Date: Sun, 17 May 2009 19:16:11 -0700 Subject: [PATCH 062/171] 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 From dbb025827992331843566be418a6f86d89f41868 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 18 May 2009 16:59:37 +0200 Subject: [PATCH 063/171] 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 From 97b75c9f1668992bd246fe46235c5764a4fa6e58 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 18 May 2009 21:27:42 +0200 Subject: [PATCH 064/171] 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 From 7a85927da21859a6868c3e0ec92267706b0a14bf Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Mon, 16 Mar 2009 13:30:30 +0100 Subject: [PATCH 065/171] 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 From 50608ecccdda7c3709c61484653f9ebb17068fcf Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 11 May 2009 23:22:20 -0400 Subject: [PATCH 066/171] 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 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/171] 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 From 9fcadcbd68a474c265ab1bd1255082c432ef6821 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 18 May 2009 14:34:32 -0700 Subject: [PATCH 068/171] 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 From 6339e5d360af1977788aca6f819762be6213772b Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Tue, 19 May 2009 10:24:26 -0400 Subject: [PATCH 069/171] 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 From 2a657725f1a31c4d88675ea01affeaed315474ef Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 19 May 2009 10:59:24 -0700 Subject: [PATCH 070/171] 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 From 542d6a0abd712be8892ef044ef40fe7bbc9631a9 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Tue, 19 May 2009 19:51:38 -0400 Subject: [PATCH 071/171] 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 From a70c78177a564c2f2cd09846a5e7ab6e8669e9f2 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Tue, 17 Mar 2009 01:04:47 +0100 Subject: [PATCH 072/171] 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 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/171] 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 From b4c7b3e893c586160ffe11aa598d729707f7f900 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 27 May 2009 14:54:58 -0500 Subject: [PATCH 074/171] 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 From 4196616778846f5ccbd8da504a2306846fd3f432 Mon Sep 17 00:00:00 2001 From: calavera Date: Wed, 27 May 2009 14:56:14 -0500 Subject: [PATCH 075/171] 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 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/171] 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 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/171] 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 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/171] 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 From c73cf7d2c0d164d819a483c83302cd4a62ca24b9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 May 2009 09:36:32 -0500 Subject: [PATCH 079/171] 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 From 34c3162c5c84a4393340e7fc5f7f7f37a828fc08 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Mon, 1 Jun 2009 13:54:20 +1200 Subject: [PATCH 080/171] 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 From a92790ab862790acc627a3194770da37e400d64b Mon Sep 17 00:00:00 2001 From: Ian Terrell Date: Tue, 17 Mar 2009 12:08:47 -0400 Subject: [PATCH 081/171] 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 From 4d7c597e84c807a082508b6d1a105a08e34f10e0 Mon Sep 17 00:00:00 2001 From: Han Kessels Date: Fri, 27 Mar 2009 15:25:27 +0900 Subject: [PATCH 082/171] 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 From b600bf2cd728c90d50cc34456c944b2dfefe8c8d Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Mon, 1 Jun 2009 14:21:08 +1200 Subject: [PATCH 083/171] 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 From 84a755b27e615da4a51b6e4c042491b6d807bcf8 Mon Sep 17 00:00:00 2001 From: Tim Connor Date: Thu, 21 May 2009 14:51:34 -0700 Subject: [PATCH 084/171] 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 From 4b4164e8a840970cfa558e0e7bf59ee1948b6506 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 00:09:50 -0700 Subject: [PATCH 085/171] 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 From 4a78dae2ab43b5ba94fa06b345e9edeb0305fe44 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 00:11:06 -0700 Subject: [PATCH 086/171] 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 From a69ee11968bfd3113e32feea26f7c39c0ba51d8a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 01:25:32 -0700 Subject: [PATCH 087/171] 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 From 01f820c3b25bfb1ec6a25c4b9668ba0fb7bc75d5 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 14:09:16 -0700 Subject: [PATCH 088/171] 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 From ec10f13939fd3b91f863878cc5aae740c67f6def Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 15:16:47 -0700 Subject: [PATCH 089/171] 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 From aebd1ba5b49248d88df3bff9ac765f847595ab34 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 20:08:02 -0700 Subject: [PATCH 090/171] 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 From 05abd7c1963758b6e98277ba360072a6308d5d1a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 20:32:40 -0700 Subject: [PATCH 091/171] 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 From 91fbdfd5b3a0c664e1b80c88e3f60235ac8f7b0e Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 20:33:18 -0700 Subject: [PATCH 092/171] 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 From 91727ae5e49b49f299942eb8181fd28312ece889 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 20:07:12 -0700 Subject: [PATCH 093/171] 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 From cc5d313a483fcfa5cceed0bd177a5b54bd5c042c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:49:12 -0700 Subject: [PATCH 094/171] 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 From f1e75e4378029d61b7b91dfc8a595dc3716bc000 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 17:58:14 -0700 Subject: [PATCH 095/171] 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 From 74f16a56e71177f2ca60f33b0ece1c2c551fc39f Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:29:24 -0700 Subject: [PATCH 096/171] 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 From 756e82d1b6fbd42d7223786cdebb981465092783 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:30:27 -0700 Subject: [PATCH 097/171] 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 From f9b22276493a127bb9099295bd6c4d8caa10de04 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:49:49 -0700 Subject: [PATCH 098/171] 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 From 63d0c337875eee99dd6fe8fc0dd6949c0a06c21c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 8 Jun 2009 19:25:56 -0700 Subject: [PATCH 099/171] 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 From e70272e2a411ce5c548e15ef107bb63749f99026 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Mon, 20 Apr 2009 00:40:15 -0700 Subject: [PATCH 100/171] 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 From 5fb66a3abb4789b00158928efb5a83c0ad0004b6 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 1 May 2009 20:12:49 +0100 Subject: [PATCH 101/171] 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 From 056ddbdcfb07f0b5c7e6ed8a35f6c3b55b4ab489 Mon Sep 17 00:00:00 2001 From: nate Date: Tue, 26 May 2009 15:06:09 -0500 Subject: [PATCH 102/171] 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 From 2c3d2906b255b1cb3aeb5d3abb9fe2dcb2872c28 Mon Sep 17 00:00:00 2001 From: Matt Jones Date: Sat, 6 Jun 2009 18:05:38 -0400 Subject: [PATCH 103/171] 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 From b1a044b6291b1045e31dc624189f38a3abc4cda2 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Tue, 9 Jun 2009 20:03:36 +1200 Subject: [PATCH 104/171] 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 From d97073337c7b9012bad4d65ef61ce8f07f8179aa Mon Sep 17 00:00:00 2001 From: Tom Ward Date: Fri, 22 May 2009 15:27:01 +0100 Subject: [PATCH 105/171] 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 From c5c022c705e4179f74e063186ea68a1c58797822 Mon Sep 17 00:00:00 2001 From: Eugene Pimenov Date: Thu, 23 Apr 2009 13:45:12 +0400 Subject: [PATCH 106/171] 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 From d63fab344f5321ea71198ac349fea1ee15773f7d Mon Sep 17 00:00:00 2001 From: Steven Luscher Date: Mon, 6 Apr 2009 20:47:23 -0700 Subject: [PATCH 107/171] 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 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/171] 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 From 898a8801ff547d18c349f069cd696926c27174e8 Mon Sep 17 00:00:00 2001 From: David Stevenson Date: Wed, 11 Mar 2009 09:28:56 -0700 Subject: [PATCH 109/171] 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 From cd14a4a00e1005c4adcf36d2f171fb52a3c08f4f Mon Sep 17 00:00:00 2001 From: Stephen Anderson Date: Tue, 6 Jan 2009 11:28:40 -0600 Subject: [PATCH 110/171] 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 From f43404d42b28c9c4e126829de5f9d815618abe5b Mon Sep 17 00:00:00 2001 From: Matt Jones Date: Tue, 9 Jun 2009 12:24:23 -0400 Subject: [PATCH 111/171] 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 From 19c38a9b70c377c0a9e3b266f2b4023ff590ce02 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 10 Jun 2009 12:10:13 +1200 Subject: [PATCH 112/171] 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 From 7e1bcef98586327ff3f6d09862c4b29cb9925bcb Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 9 Jun 2009 23:51:04 -0700 Subject: [PATCH 113/171] 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 From d3d4822262859cc773f28257bd1e5159b69ccb82 Mon Sep 17 00:00:00 2001 From: Andrew Kaspick Date: Thu, 4 Jun 2009 22:50:41 -0500 Subject: [PATCH 114/171] 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 From 25fde77674cd6f9906b7a8e7eef046f06698c02a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 11 Jun 2009 19:39:21 -0500 Subject: [PATCH 115/171] 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 From cb9429a259af819b4b1356e972388137109189de Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 11 Jun 2009 22:34:23 -0700 Subject: [PATCH 116/171] 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 From 447d60e9ed015e5d6bd336323881f8180c2070d0 Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Sat, 13 Jun 2009 12:43:07 +0200 Subject: [PATCH 117/171] 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 From a491b19502781266b05918cf99b6ba67898e3be9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 15 Jun 2009 10:23:23 -0500 Subject: [PATCH 118/171] 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 From b75bc05bc540f2e9efaf41d3a1540787aaa58d19 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 18 Jun 2009 18:06:42 -0400 Subject: [PATCH 119/171] 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 From 8b9b954f40ef6fdd2ca8c3eb50e35eda9eb115a6 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 18 Jun 2009 20:00:24 -0400 Subject: [PATCH 120/171] 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 From 22554745b7239eca19ea634feca4fcda517b3f97 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 18 Jun 2009 21:31:10 -0400 Subject: [PATCH 121/171] 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 From 6720b25b2da5e1b81494d4dcc6248f64479a5db8 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 25 Jun 2009 14:44:09 -0500 Subject: [PATCH 122/171] 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 From 9407f6e9a428b37517fdd779eac15e4f77bb4b71 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sat, 27 Jun 2009 13:11:01 +1200 Subject: [PATCH 123/171] 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 From eb52dc3db7ebab5327869ab33804259912ef10be Mon Sep 17 00:00:00 2001 From: Chris Mear Date: Thu, 19 Feb 2009 14:16:10 +0000 Subject: [PATCH 124/171] 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 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/171] 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 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/171] 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 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/171] 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 From 5217c16b094a07919348b52aa164ca2fd26b5d5e Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 1 Jul 2009 16:22:17 -0700 Subject: [PATCH 128/171] 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 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/171] 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 From 0d3c5f0a822cd1b6029b5f619774b7794a94f370 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Sun, 28 Jun 2009 02:14:44 -0500 Subject: [PATCH 130/171] 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 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/171] 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 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/171] 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 From d8fff7d9d5ac4283fcabd7ecf64af5709410424f Mon Sep 17 00:00:00 2001 From: Levin Alexander Date: Thu, 25 Jun 2009 22:47:27 +0200 Subject: [PATCH 133/171] 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 From 1c855ad4e7c14fbf08edc1f1eb9b3c6fe186b701 Mon Sep 17 00:00:00 2001 From: Jarl Friis Date: Mon, 11 May 2009 14:09:22 +0200 Subject: [PATCH 134/171] 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 From 944f4fc7d2ee8fc54008dfda1014983334427edc Mon Sep 17 00:00:00 2001 From: Brian Abreu Date: Wed, 24 Jun 2009 10:51:20 -0700 Subject: [PATCH 135/171] 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 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/171] 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 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/171] 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 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/171] 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 From 29c59858492d44cc737e1f0b944a51fe867261f8 Mon Sep 17 00:00:00 2001 From: Lawrence Pit Date: Mon, 6 Jul 2009 11:42:41 +1000 Subject: [PATCH 139/171] 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 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/171] 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 From 6a50d6c8e39bcc25df3d93226006b4523f8a2cb6 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 8 Jul 2009 17:30:33 +1200 Subject: [PATCH 141/171] 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 From 18e68d95240df673dc62af240a1eb63019a371f7 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 12 Jul 2009 15:43:39 +1200 Subject: [PATCH 142/171] 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 From f36d9a67582a857720aecf835917748163baa5a6 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 12 Jul 2009 19:28:04 -0500 Subject: [PATCH 143/171] 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 From ae85927ea84a0e16d2fe9ba156de537b7935a943 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 12 Jul 2009 19:31:41 -0500 Subject: [PATCH 144/171] 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 From b3ec7b2d03a52e43a4451d522eea7e6499289daa Mon Sep 17 00:00:00 2001 From: Szymon Nowak Date: Wed, 15 Jul 2009 22:29:29 +0200 Subject: [PATCH 145/171] 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 From a147becfb86b689ab25e92edcfbb4bcc04108099 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 15 Jul 2009 15:44:45 +1200 Subject: [PATCH 146/171] 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 From c7bcbb983f84e306e2b43adb698060e060605de9 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 19 Jul 2009 17:27:45 +1200 Subject: [PATCH 147/171] 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 From 7a427a83ca4da92c70760007aaf313638a5d8374 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sun, 19 Jul 2009 14:13:28 +0900 Subject: [PATCH 148/171] 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 From be4d743645b47927b442ed2816ca0fa33421561e Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sun, 19 Jul 2009 14:36:44 +0900 Subject: [PATCH 149/171] 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 From 143c55d325388aa2384de8f137cfb61c09912208 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 22 Jul 2009 09:46:26 +1200 Subject: [PATCH 150/171] 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 From ead5d88bf178b8a32d151747a42df2aafa57eeaa Mon Sep 17 00:00:00 2001 From: Sebastian Delmont Date: Mon, 20 Jul 2009 17:53:23 -0400 Subject: [PATCH 151/171] 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 From 60122e81a3504ed171a761573ed16cb034c79c13 Mon Sep 17 00:00:00 2001 From: Luke Melia Date: Sat, 30 May 2009 09:34:57 -0500 Subject: [PATCH 152/171] 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 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/171] 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 From 17f336e2f00f419a41eb7effb817bd7ad3e84f0d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 29 Jul 2009 16:53:49 -0700 Subject: [PATCH 154/171] 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 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/171] 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 From 32cfd4c2f8cebf3dd331b2f830a268dbf637adf3 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 1 Aug 2009 18:21:11 -0700 Subject: [PATCH 156/171] 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 From 21029451d7423b51be7afb96e1b6647b87c5be5c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 1 Aug 2009 19:50:54 -0700 Subject: [PATCH 157/171] 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 From 7244425b93ded0789b73681533df84ffcd562852 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 1 Aug 2009 18:34:41 -0700 Subject: [PATCH 158/171] 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 From dc559f274fbd1ba180a785e741b9b2d7a7d09ae7 Mon Sep 17 00:00:00 2001 From: Sava Chankov Date: Sat, 1 Aug 2009 19:38:05 -0700 Subject: [PATCH 159/171] 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 From f09ceb55c0eaa8f81d5cb426501a32f86f96b23c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 1 Aug 2009 20:07:28 -0700 Subject: [PATCH 160/171] 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 From 6cbcfffeb118ecbb7c1930c74de7b8b6a49dfacd Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 1 Aug 2009 20:18:22 -0700 Subject: [PATCH 161/171] 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 From bf00de03dee5fba0b53f0fc1bb19464422550aa9 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 8 Jul 2009 18:49:40 +0200 Subject: [PATCH 162/171] 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 From 95db8aaa5f395b77e4a54a6cc1bed741251dc81f Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Mon, 27 Apr 2009 16:50:20 +1200 Subject: [PATCH 163/171] 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 From 5a0e2959117e29e6836b9f611d6ba1d3260702b7 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 5 Aug 2009 19:40:11 +0100 Subject: [PATCH 164/171] 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 From 55501b9f6ab46d45db04a81956579402511ad092 Mon Sep 17 00:00:00 2001 From: rick Date: Wed, 5 Aug 2009 16:27:02 -0700 Subject: [PATCH 165/171] 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 From 8dab61d146f26c18acdef1fcc57b01612f96440c Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Thu, 6 Aug 2009 08:34:23 +0900 Subject: [PATCH 166/171] 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 From e1d27eedceea46531d11c03c1e1e61ba15a5fdf2 Mon Sep 17 00:00:00 2001 From: Brendan Schwartz Date: Wed, 25 Mar 2009 15:14:52 -0400 Subject: [PATCH 167/171] 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 From f73d34c131c1e371c76c5a146aac2c2bffbf96e5 Mon Sep 17 00:00:00 2001 From: Matt Duncan Date: Fri, 7 Aug 2009 21:37:21 -0400 Subject: [PATCH 168/171] 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 From 1c6c216d911cc55ef7a338d2aab5c827ca6f3468 Mon Sep 17 00:00:00 2001 From: Mike Breen Date: Mon, 20 Jul 2009 16:48:08 -0400 Subject: [PATCH 169/171] 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 From 9aaeb18781f329bf04c3174898bb8f32581a515e Mon Sep 17 00:00:00 2001 From: Josh Sharpe Date: Fri, 7 Aug 2009 21:12:27 -0400 Subject: [PATCH 170/171] 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 From fca9b55b6c6dadb00c134c079d7d24ed9991024d Mon Sep 17 00:00:00 2001 From: Steve St. Martin Date: Sat, 8 Aug 2009 14:42:14 -0400 Subject: [PATCH 171/171] truncate should not use omission in length calculation --- actionpack/lib/action_view/helpers/text_helper.rb | 14 +++++++------- actionpack/test/template/text_helper_test.rb | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 8463af9..9024652 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -33,30 +33,30 @@ 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 appended with the :omission (defaults to "..."). # # ==== 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 far... # # truncate("Once upon a time in a world far far away", :length => 14) - # # => Once upon a... + # # => Once upon a ti... # # truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)") # # => And they found that many (clipped) # # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 15) - # # => And they found... (continued) + # # => And they found ... (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 ti... # # truncate("And they found that many people were sleeping better.", 15, "... (continued)") - # # => And they found... (continued) + # # => And they found ... (continued) def truncate(text, *args) options = args.extract_options! unless args.empty? @@ -69,7 +69,7 @@ module ActionView options.reverse_merge!(:length => 30, :omission => "...") if text - l = options[:length] - options[:omission].mb_chars.length + l = options[:length] chars = text.mb_chars (chars.length > options[:length] ? chars[0...l] + options[:omission] : text).to_s end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index a370f14..6b2e64e 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -36,36 +36,36 @@ class TextHelperTest < ActionView::TestCase def test_truncate assert_equal "Hello World!", truncate("Hello World!", :length => 12) - assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12) + assert_equal "Hello World!...", truncate("Hello World!!", :length => 12) end def test_truncate_should_use_default_length_of_30 str = "This is a string that will go longer then the default truncate length of 30" - assert_equal str[0...27] + "...", truncate(str) + assert_equal str[0...30] + "...", truncate(str) end def test_truncate_with_options_hash - assert_equal "This is a string that wil[...]", truncate("This is a string that will go longer then the default truncate length of 30", :omission => "[...]") - assert_equal "Hello W...", truncate("Hello World!", :length => 10) - assert_equal "Hello[...]", truncate("Hello World!", :omission => "[...]", :length => 10) + assert_equal "This is a string that will go [...]", truncate("This is a string that will go longer then the default truncate length of 30", :omission => "[...]") + assert_equal "Hello Worl...", truncate("Hello World!", :length => 10) + assert_equal "Hello Worl[...]", truncate("Hello World!", :omission => "[...]", :length => 10) end if RUBY_VERSION < '1.9.0' def test_truncate_multibyte with_kcode 'none' do - assert_equal "\354\225\210\353\205\225\355...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10) + assert_equal "\354\225\210\353\205\225\355\225\230\354...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10) end with_kcode 'u' do - assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...", + assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254...", truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244", :length => 10) end end else def test_truncate_multibyte - assert_equal "\354\225\210\353\205\225\355...", + assert_equal "\354\225\210\353\205\225\355\225\230\354...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10) - assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'), + assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254...".force_encoding('UTF-8'), truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10) end end @@ -392,7 +392,7 @@ class TextHelperTest < ActionView::TestCase url = "http://api.rubyonrails.com/Foo.html" email = "fantabulous@shiznadel.ic" - assert_equal %(

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

), auto_link("

#{url}
#{email}

") { |url| truncate(url, :length => 10) } + assert_equal %(

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

), auto_link("

#{url}
#{email}

") { |url| truncate(url, :length => 10) } end def test_auto_link_with_options_hash -- 1.6.4