Merge branch 'master' into arel

This commit is contained in:
Jeremy Kemper
2009-10-14 17:24:17 -07:00
39 changed files with 933 additions and 246 deletions

View File

@@ -87,8 +87,9 @@ module ActionController #:nodoc:
def delete(key, options = {})
options.symbolize_keys!
options[:path] = "/" unless options.has_key?(:path)
super(key.to_s)
value = super(key.to_s)
@controller.response.delete_cookie(key, options)
value
end
end
end

View File

@@ -16,7 +16,7 @@ module ActionController
logger.info("Redirected to #{url}") if logger && logger.info?
self.status = status
self.location = url.gsub(/[\r\n]/, '')
self.response_body = "<html><body>You are being <a href=\"#{CGI.escapeHTML(url)}\">redirected</a>.</body></html>"
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(url)}\">redirected</a>.</body></html>"
end
end
end

View File

@@ -35,7 +35,7 @@ module ActionController #:nodoc:
end
def cookies
@response.cookies
@request.cookies.merge(@response.cookies)
end
def redirect_to_url

View File

@@ -396,8 +396,12 @@ module ActionDispatch
# Delegate unhandled messages to the current session instance.
def method_missing(sym, *args, &block)
reset! unless @integration_session
returning @integration_session.__send__(sym, *args, &block) do
copy_session_variables!
if @integration_session.respond_to?(sym)
returning @integration_session.__send__(sym, *args, &block) do
copy_session_variables!
end
else
super
end
end
end

View File

@@ -167,7 +167,7 @@ module ActionView #:nodoc:
module Subclasses
end
include Helpers, Rendering, Partials, ::ERB::Util
include Helpers, Rendering, Partials, ::ERB::Util, ActiveSupport::Configurable
extend ActiveSupport::Memoizable

View File

@@ -133,9 +133,13 @@ module ActionView
# change. You can use something like Live HTTP Headers for Firefox to verify
# that the cache is indeed working.
module AssetTagHelper
ASSETS_DIR = defined?(Rails.public_path) ? Rails.public_path : "public"
JAVASCRIPTS_DIR = "#{ASSETS_DIR}/javascripts"
STYLESHEETS_DIR = "#{ASSETS_DIR}/stylesheets"
assets_dir = defined?(Rails.public_path) ? Rails.public_path : "public"
ActionView::DEFAULT_CONFIG = {
:assets_dir => assets_dir,
:javascripts_dir => "#{assets_dir}/javascripts",
:stylesheets_dir => "#{assets_dir}/stylesheets",
}
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
# Returns a link tag that browsers and news readers can use to auto-detect
@@ -280,7 +284,7 @@ module ActionView
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)
joined_javascript_path = File.join(joined_javascript_name[/^#{File::SEPARATOR}/] ? config.assets_dir : config.javascripts_dir, joined_javascript_name)
unless ActionController::Base.perform_caching && File.exists?(joined_javascript_path)
write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive))
@@ -431,7 +435,7 @@ module ActionView
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)
joined_stylesheet_path = File.join(joined_stylesheet_name[/^#{File::SEPARATOR}/] ? config.assets_dir : config.stylesheets_dir, joined_stylesheet_name)
unless ActionController::Base.perform_caching && File.exists?(joined_stylesheet_path)
write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive))
@@ -630,11 +634,11 @@ module ActionView
# Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
# roots. Rewrite the asset path for cache-busting asset ids. Include
# asset host, if configured, with the correct request protocol.
def compute_public_path(source, dir, ext = nil, include_host = true)
def compute_public_path(source, dir, ext = nil, include_host = true)
has_request = @controller.respond_to?(:request)
source_ext = File.extname(source)[1..-1]
if ext && !is_uri?(source) && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))))
if ext && !is_uri?(source) && (source_ext.blank? || (ext != source_ext && File.exist?(File.join(config.assets_dir, dir, "#{source}.#{ext}"))))
source += ".#{ext}"
end
@@ -700,7 +704,7 @@ module ActionView
if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source])
asset_id
else
path = File.join(ASSETS_DIR, source)
path = File.join(config.assets_dir, source)
asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
if @@cache_asset_timestamps
@@ -743,20 +747,20 @@ module ActionView
def expand_javascript_sources(sources, recursive = false)
if sources.include?(:all)
all_javascript_files = collect_asset_files(JAVASCRIPTS_DIR, ('**' if recursive), '*.js')
all_javascript_files = collect_asset_files(config.javascripts_dir, ('**' if recursive), '*.js')
((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq
else
expanded_sources = sources.collect do |source|
determine_source(source, @@javascript_expansions)
end.flatten
expanded_sources << "application" if sources.include?(:defaults) && File.exist?(File.join(JAVASCRIPTS_DIR, "application.js"))
expanded_sources << "application" if sources.include?(:defaults) && File.exist?(File.join(config.javascripts_dir, "application.js"))
expanded_sources
end
end
def expand_stylesheet_sources(sources, recursive)
if sources.first == :all
collect_asset_files(STYLESHEETS_DIR, ('**' if recursive), '*.css')
collect_asset_files(config.stylesheets_dir, ('**' if recursive), '*.css')
else
sources.collect do |source|
determine_source(source, @@stylesheet_expansions)
@@ -803,7 +807,7 @@ module ActionView
end
def asset_file_path(path)
File.join(ASSETS_DIR, path.split('?').first)
File.join(config.assets_dir, path.split('?').first)
end
def asset_file_path!(path)

View File

@@ -83,7 +83,7 @@ module ActionView
options
when Hash
options = { :only_path => options[:host].nil? }.update(options.symbolize_keys)
escape = options.key?(:escape) ? options.delete(:escape) : true
escape = options.key?(:escape) ? options.delete(:escape) : false
@controller.send(:url_for, options)
when :back
escape = false
@@ -93,7 +93,7 @@ module ActionView
polymorphic_path(options)
end
(escape ? escape_once(url) : url).html_safe!
escape ? escape_once(url).html_safe! : url
end
# Creates a link tag of the given +name+ using a URL created by the set

View File

@@ -5,7 +5,7 @@ module ActionView #:nodoc:
if value.html_safe?
super(value)
else
super(CGI.escapeHTML(value))
super(ERB::Util.h(value))
end
end

View File

@@ -118,6 +118,13 @@ class CookieTest < ActionController::TestCase
assert_equal %w{1 2 3}, jar["pages"]
end
def test_cookiejar_delete_removes_item_and_returns_its_value
@request.cookies["user_name"] = "david"
@controller.response = @response
jar = ActionController::CookieJar.new(@controller)
assert_equal "david", jar.delete("user_name")
end
def test_delete_cookie_with_path
get :delete_cookie_with_path
assert_cookie_header "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"

View File

@@ -199,6 +199,24 @@ class IntegrationTestTest < Test::Unit::TestCase
assert_equal ::ActionController::Integration::Session, session2.class
assert_not_equal session1, session2
end
# RSpec mixes Matchers (which has a #method_missing) into
# IntegrationTest's superclass. Make sure IntegrationTest does not
# try to delegate these methods to the session object.
def test_does_not_prevent_method_missing_passing_up_to_ancestors
mixin = Module.new do
def method_missing(name, *args)
name.to_s == 'foo' ? 'pass' : super
end
end
@test.class.superclass.__send__(:include, mixin)
begin
assert_equal 'pass', @test.foo
ensure
# leave other tests as unaffected as possible
mixin.__send__(:remove_method, :method_missing)
end
end
end
# Tests that integration tests don't call Controller test methods for processing.

View File

@@ -108,6 +108,11 @@ XML
head :created, :location => 'created resource'
end
def delete_cookie
cookies.delete("foo")
render :nothing => true
end
private
def rescue_action(e)
raise e
@@ -512,6 +517,18 @@ XML
assert @request.params[:foo].blank?
end
def test_should_have_knowledge_of_client_side_cookie_state_even_if_they_are_not_set
@request.cookies['foo'] = 'bar'
get :no_op
assert_equal 'bar', cookies['foo']
end
def test_should_detect_if_cookie_is_deleted
@request.cookies['foo'] = 'bar'
get :delete_cookie
assert_nil cookies['foo']
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

View File

@@ -26,11 +26,11 @@ class ActionController::TestSessionTest < ActiveSupport::TestCase
assert_equal('value', session[:key])
end
def test_calling_delete_removes_item
def test_calling_delete_removes_item_and_returns_its_value
session = ActionController::TestSession.new
session[:key] = 'value'
assert_equal('value', session[:key])
session.delete(:key)
assert_equal('value', session.delete(:key))
assert_nil(session[:key])
end

View File

@@ -3,6 +3,13 @@ require 'abstract_unit'
class AssetTagHelperTest < ActionView::TestCase
tests ActionView::Helpers::AssetTagHelper
DEFAULT_CONFIG = ActionView::DEFAULT_CONFIG.merge(
:assets_dir => File.dirname(__FILE__) + "/../fixtures/public",
:javascripts_dir => File.dirname(__FILE__) + "/../fixtures/public/javascripts",
:stylesheets_dir => File.dirname(__FILE__) + "/../fixtures/public/stylesheets")
include ActiveSupport::Configurable
def setup
super
silence_warnings do
@@ -872,6 +879,9 @@ end
class AssetTagHelperNonVhostTest < ActionView::TestCase
tests ActionView::Helpers::AssetTagHelper
DEFAULT_CONFIG = ActionView::DEFAULT_CONFIG
include ActiveSupport::Configurable
def setup
super
ActionController::Base.relative_url_root = "/collaboration/hieraki"

View File

@@ -3,6 +3,9 @@ require 'abstract_unit'
class FormTagHelperTest < ActionView::TestCase
tests ActionView::Helpers::FormTagHelper
include ActiveSupport::Configurable
DEFAULT_CONFIG = ActionView::DEFAULT_CONFIG
def setup
super
@controller = Class.new do

View File

@@ -5,6 +5,9 @@ require 'controller/fake_controllers'
RequestMock = Struct.new("Request", :request_uri, :protocol, :host_with_port, :env)
class UrlHelperTest < ActionView::TestCase
include ActiveSupport::Configurable
DEFAULT_CONFIG = ActionView::DEFAULT_CONFIG
def setup
super
@controller = Class.new do
@@ -19,10 +22,15 @@ class UrlHelperTest < ActionView::TestCase
def test_url_for_escapes_urls
@controller.url = "http://www.example.com?a=b&c=d"
assert_equal "http://www.example.com?a=b&amp;c=d", url_for(:a => 'b', :c => 'd')
assert_equal "http://www.example.com?a=b&c=d", url_for(:a => 'b', :c => 'd')
assert_equal "http://www.example.com?a=b&amp;c=d", url_for(:a => 'b', :c => 'd', :escape => true)
assert_equal "http://www.example.com?a=b&c=d", url_for(:a => 'b', :c => 'd', :escape => false)
end
def test_url_for_escaping_is_safety_aware
assert url_for(:a => 'b', :c => 'd', :escape => true).html_safe?, "escaped urls should be html_safe?"
assert !url_for(:a => 'b', :c => 'd', :escape => false).html_safe?, "non-escaped urls shouldn't be safe"
end
def test_url_for_escapes_url_once
@controller.url = "http://www.example.com?a=b&amp;c=d"
@@ -39,6 +47,16 @@ class UrlHelperTest < ActionView::TestCase
assert_equal 'javascript:history.back()', url_for(:back)
end
def test_url_for_from_hash_doesnt_escape_ampersand
@controller = TestController.new
@view = ActionView::Base.new
@view.controller = @controller
path = @view.url_for(:controller => :cheeses, :foo => :bar, :baz => :quux)
assert_equal '/cheeses?baz=quux&foo=bar', path
end
# todo: missing test cases
def test_button_to_with_straight_url
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
@@ -295,7 +313,7 @@ class UrlHelperTest < ActionView::TestCase
@controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc&page=1")
@controller.url = "http://www.example.com/weblog/show?order=desc&page=1"
assert_equal "Showing", link_to_unless_current("Showing", { :action => "show", :controller => "weblog", :order=>'desc', :page=>'1' })
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=desc&amp;page=1")
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=desc&page=1")
assert_equal "Showing", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=desc&page=1")
@controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc")
@@ -305,7 +323,7 @@ class UrlHelperTest < ActionView::TestCase
@controller.request = RequestMock.new("http://www.example.com/weblog/show?order=desc&page=1")
@controller.url = "http://www.example.com/weblog/show?order=desc&page=2"
assert_equal "<a href=\"http://www.example.com/weblog/show?order=desc&amp;page=2\">Showing</a>", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
assert_equal "<a href=\"http://www.example.com/weblog/show?order=desc&page=2\">Showing</a>", link_to_unless_current("Showing", { :action => "show", :controller => "weblog" })
assert_equal "<a href=\"http://www.example.com/weblog/show?order=desc&amp;page=2\">Showing</a>", link_to_unless_current("Showing", "http://www.example.com/weblog/show?order=desc&page=2")

View File

@@ -326,6 +326,17 @@ module ActiveResource
@password = password
end
def auth_type
if defined?(@auth_type)
@auth_type
end
end
def auth_type=(auth_type)
@connection = nil
@auth_type = auth_type
end
# Sets the format that attributes are sent and received in from a mime type reference:
#
# Person.format = :json
@@ -397,6 +408,7 @@ module ActiveResource
@connection.proxy = proxy if proxy
@connection.user = user if user
@connection.password = password if password
@connection.auth_type = auth_type if auth_type
@connection.timeout = timeout if timeout
@connection.ssl_options = ssl_options if ssl_options
@connection

View File

@@ -17,7 +17,7 @@ module ActiveResource
:head => 'Accept'
}
attr_reader :site, :user, :password, :timeout, :proxy, :ssl_options
attr_reader :site, :user, :password, :auth_type, :timeout, :proxy, :ssl_options
attr_accessor :format
class << self
@@ -57,6 +57,11 @@ module ActiveResource
@password = password
end
# Sets the auth type for remote service.
def auth_type=(auth_type)
@auth_type = legitimize_auth_type(auth_type)
end
# Sets the number of seconds after which HTTP requests to the remote service should time out.
def timeout=(timeout)
@timeout = timeout
@@ -70,31 +75,31 @@ module ActiveResource
# Executes a GET request.
# Used to get (find) resources.
def get(path, headers = {})
format.decode(request(:get, path, build_request_headers(headers, :get)).body)
with_auth { format.decode(request(:get, path, build_request_headers(headers, :get, self.site.merge(path))).body) }
end
# Executes a DELETE request (see HTTP protocol documentation if unfamiliar).
# Used to delete resources.
def delete(path, headers = {})
request(:delete, path, build_request_headers(headers, :delete))
with_auth { request(:delete, path, build_request_headers(headers, :delete, self.site.merge(path))) }
end
# Executes a PUT request (see HTTP protocol documentation if unfamiliar).
# Used to update resources.
def put(path, body = '', headers = {})
request(:put, path, body.to_s, build_request_headers(headers, :put))
with_auth { request(:put, path, body.to_s, build_request_headers(headers, :put, self.site.merge(path))) }
end
# Executes a POST request.
# Used to create new resources.
def post(path, body = '', headers = {})
request(:post, path, body.to_s, build_request_headers(headers, :post))
with_auth { request(:post, path, body.to_s, build_request_headers(headers, :post, self.site.merge(path))) }
end
# Executes a HEAD request.
# Used to obtain meta-information about resources, such as whether they exist and their size (via response headers).
def head(path, headers = {})
request(:head, path, build_request_headers(headers, :head))
with_auth { request(:head, path, build_request_headers(headers, :head, self.site.merge(path))) }
end
@@ -198,13 +203,70 @@ module ActiveResource
end
# Builds headers for request to remote service.
def build_request_headers(headers, http_method=nil)
authorization_header.update(default_header).update(http_format_header(http_method)).update(headers)
def build_request_headers(headers, http_method, uri)
authorization_header(http_method, uri).update(default_header).update(http_format_header(http_method)).update(headers)
end
# Sets authorization header
def authorization_header
(@user || @password ? { 'Authorization' => 'Basic ' + ["#{@user}:#{ @password}"].pack('m').delete("\r\n") } : {})
def response_auth_header
@response_auth_header ||= ""
end
def with_auth
retried ||= false
yield
rescue UnauthorizedAccess => e
raise if retried || auth_type != :digest
@response_auth_header = e.response['WWW-Authenticate']
retried = true
retry
end
def authorization_header(http_method, uri)
if @user || @password
if auth_type == :digest
{ 'Authorization' => digest_auth_header(http_method, uri) }
else
{ 'Authorization' => 'Basic ' + ["#{@user}:#{@password}"].pack('m').delete("\r\n") }
end
else
{}
end
end
def digest_auth_header(http_method, uri)
params = extract_params_from_response
ha1 = Digest::MD5.hexdigest("#{@user}:#{params['realm']}:#{@password}")
ha2 = Digest::MD5.hexdigest("#{http_method.to_s.upcase}:#{uri.path}")
params.merge!('cnonce' => client_nonce)
request_digest = Digest::MD5.hexdigest([ha1, params['nonce'], "0", params['cnonce'], params['qop'], ha2].join(":"))
"Digest #{auth_attributes_for(uri, request_digest, params)}"
end
def client_nonce
Digest::MD5.hexdigest("%x" % (Time.now.to_i + rand(65535)))
end
def extract_params_from_response
params = {}
if response_auth_header =~ /^(\w+) (.*)/
$2.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
end
params
end
def auth_attributes_for(uri, request_digest, params)
[
%Q(username="#{@user}"),
%Q(realm="#{params['realm']}"),
%Q(qop="#{params['qop']}"),
%Q(uri="#{uri.path}"),
%Q(nonce="#{params['nonce']}"),
%Q(nc="0"),
%Q(cnonce="#{params['cnonce']}"),
%Q(opaque="#{params['opaque']}"),
%Q(response="#{request_digest}")].join(", ")
end
def http_format_header(http_method)
@@ -214,5 +276,11 @@ module ActiveResource
def logger #:nodoc:
Base.logger
end
def legitimize_auth_type(auth_type)
return :basic if auth_type.nil?
auth_type = auth_type.to_sym
[:basic, :digest].include?(auth_type) ? auth_type : :basic
end
end
end

View File

@@ -1,5 +1,6 @@
require 'rubygems'
require 'test/unit'
require 'active_support'
require 'active_support/test_case'
$:.unshift "#{File.dirname(__FILE__)}/../lib"

View File

@@ -8,46 +8,75 @@ class AuthorizationTest < Test::Unit::TestCase
@matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
@david = { :id => 2, :name => 'David' }.to_xml(:root => 'person')
@authenticated_conn = ActiveResource::Connection.new("http://david:test123@localhost")
@authorization_request_header = { 'Authorization' => 'Basic ZGF2aWQ6dGVzdDEyMw==' }
@basic_authorization_request_header = { 'Authorization' => 'Basic ZGF2aWQ6dGVzdDEyMw==' }
@nonce = "MTI0OTUxMzc4NzpjYWI3NDM3NDNmY2JmODU4ZjQ2ZjcwNGZkMTJiMjE0NA=="
ActiveResource::HttpMock.respond_to do |mock|
mock.get "/people/2.xml", @authorization_request_header, @david
mock.put "/people/2.xml", @authorization_request_header, nil, 204
mock.delete "/people/2.xml", @authorization_request_header, nil, 200
mock.post "/people/2/addresses.xml", @authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5'
mock.get "/people/2.xml", @basic_authorization_request_header, @david
mock.get "/people/1.xml", @basic_authorization_request_header, nil, 401, { 'WWW-Authenticate' => 'i_should_be_ignored' }
mock.put "/people/2.xml", @basic_authorization_request_header, nil, 204
mock.delete "/people/2.xml", @basic_authorization_request_header, nil, 200
mock.post "/people/2/addresses.xml", @basic_authorization_request_header, nil, 201, 'Location' => '/people/1/addresses/5'
mock.head "/people/2.xml", @basic_authorization_request_header, nil, 200
mock.get "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "a10c9bd131c9d4d7755b8f4706fd04af") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
mock.get "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "912c7a643f18cda562b8d9662c47b6f5") }, @david, 200
mock.get "/people/1.xml", { 'Authorization' => request_digest_auth_header("/people/1.xml", "d76e675c0ecfa2bb1abe01491b068a06") }, @matz, 200
mock.put "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "7de8a265a5be3c4c2d3a246562ecd6bd") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
mock.put "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "3fb3b33d9d0b869cc75815aa11faacd9") }, nil, 204
mock.delete "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "07dfc32769a34ea3510d3a77d64ca495") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
mock.delete "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "5d438610de7ec163b29096c9afcbb254") }, nil, 200
mock.post "/people/2/addresses.xml", { 'Authorization' => blank_digest_auth_header("/people/2/addresses.xml", "966dab13620421f928d051f2b9d7b9af") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
mock.post "/people/2/addresses.xml", { 'Authorization' => request_digest_auth_header("/people/2/addresses.xml", "ed540d032c63f8ee34959116c090ec45") }, nil, 201, 'Location' => '/people/1/addresses/5'
mock.head "/people/2.xml", { 'Authorization' => blank_digest_auth_header("/people/2.xml", "2854eeb92cce2aed29350ea0ce7ba1e2") }, nil, 401, { 'WWW-Authenticate' => response_digest_auth_header }
mock.head "/people/2.xml", { 'Authorization' => request_digest_auth_header("/people/2.xml", "07cd4d247e9c130f92ba2501a080b328") }, nil, 200
end
# Make client nonce deterministic
class << @authenticated_conn
private
def client_nonce
'i-am-a-client-nonce'
end
end
end
def test_authorization_header
authorization_header = @authenticated_conn.__send__(:authorization_header)
assert_equal @authorization_request_header['Authorization'], authorization_header['Authorization']
authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization']
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
assert_equal ["david", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_with_username_but_no_password
@conn = ActiveResource::Connection.new("http://david:@localhost")
authorization_header = @conn.__send__(:authorization_header)
authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
assert_equal ["david"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_with_password_but_no_username
@conn = ActiveResource::Connection.new("http://:test123@localhost")
authorization_header = @conn.__send__(:authorization_header)
authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
assert_equal ["", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_with_decoded_credentials_from_url
@conn = ActiveResource::Connection.new("http://my%40email.com:%31%32%33@localhost")
authorization_header = @conn.__send__(:authorization_header)
authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
@@ -58,8 +87,8 @@ class AuthorizationTest < Test::Unit::TestCase
@authenticated_conn = ActiveResource::Connection.new("http://@localhost")
@authenticated_conn.user = 'david'
@authenticated_conn.password = 'test123'
authorization_header = @authenticated_conn.__send__(:authorization_header)
assert_equal @authorization_request_header['Authorization'], authorization_header['Authorization']
authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization']
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
@@ -69,7 +98,7 @@ class AuthorizationTest < Test::Unit::TestCase
def test_authorization_header_explicitly_setting_username_but_no_password
@conn = ActiveResource::Connection.new("http://@localhost")
@conn.user = "david"
authorization_header = @conn.__send__(:authorization_header)
authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
@@ -79,38 +108,119 @@ class AuthorizationTest < Test::Unit::TestCase
def test_authorization_header_explicitly_setting_password_but_no_username
@conn = ActiveResource::Connection.new("http://@localhost")
@conn.password = "test123"
authorization_header = @conn.__send__(:authorization_header)
authorization_header = @conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
assert_equal ["", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_if_credentials_supplied_and_auth_type_is_basic
@authenticated_conn.auth_type = :basic
authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
assert_equal @basic_authorization_request_header['Authorization'], authorization_header['Authorization']
authorization = authorization_header["Authorization"].to_s.split
assert_equal "Basic", authorization[0]
assert_equal ["david", "test123"], ActiveSupport::Base64.decode64(authorization[1]).split(":")[0..1]
end
def test_authorization_header_if_credentials_supplied_and_auth_type_is_digest
@authenticated_conn.auth_type = :digest
authorization_header = @authenticated_conn.__send__(:authorization_header, :get, URI.parse('/people/2.xml'))
assert_equal blank_digest_auth_header("/people/2.xml", "a10c9bd131c9d4d7755b8f4706fd04af"), authorization_header['Authorization']
end
def test_get
david = @authenticated_conn.get("/people/2.xml")
assert_equal "David", david["name"]
end
def test_post
response = @authenticated_conn.post("/people/2/addresses.xml")
assert_equal "/people/1/addresses/5", response["Location"]
end
def test_put
response = @authenticated_conn.put("/people/2.xml")
assert_equal 204, response.code
end
def test_delete
response = @authenticated_conn.delete("/people/2.xml")
assert_equal 200, response.code
end
def test_head
response = @authenticated_conn.head("/people/2.xml")
assert_equal 200, response.code
end
def test_get_with_digest_auth_handles_initial_401_response_and_retries
@authenticated_conn.auth_type = :digest
response = @authenticated_conn.get("/people/2.xml")
assert_equal "David", response["name"]
end
def test_post_with_digest_auth_handles_initial_401_response_and_retries
@authenticated_conn.auth_type = :digest
response = @authenticated_conn.post("/people/2/addresses.xml")
assert_equal "/people/1/addresses/5", response["Location"]
assert_equal 201, response.code
end
def test_put_with_digest_auth_handles_initial_401_response_and_retries
@authenticated_conn.auth_type = :digest
response = @authenticated_conn.put("/people/2.xml")
assert_equal 204, response.code
end
def test_delete_with_digest_auth_handles_initial_401_response_and_retries
@authenticated_conn.auth_type = :digest
response = @authenticated_conn.delete("/people/2.xml")
assert_equal 200, response.code
end
def test_head_with_digest_auth_handles_initial_401_response_and_retries
@authenticated_conn.auth_type = :digest
response = @authenticated_conn.head("/people/2.xml")
assert_equal 200, response.code
end
def test_get_with_digest_auth_caches_nonce
@authenticated_conn.auth_type = :digest
response = @authenticated_conn.get("/people/2.xml")
assert_equal "David", response["name"]
# There is no mock for this request with a non-cached nonce.
response = @authenticated_conn.get("/people/1.xml")
assert_equal "Matz", response["name"]
end
def test_retry_on_401_only_happens_with_digest_auth
assert_raise(ActiveResource::UnauthorizedAccess) { @authenticated_conn.get("/people/1.xml") }
assert_equal "", @authenticated_conn.send(:response_auth_header)
end
def test_raises_invalid_request_on_unauthorized_requests
assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.put("/people/2.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.xml") }
end
def test_raises_invalid_request_on_unauthorized_requests_with_digest_auth
@conn.auth_type = :digest
assert_raise(ActiveResource::InvalidRequestError) { @conn.get("/people/2.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.post("/people/2/addresses.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.put("/people/2.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.delete("/people/2.xml") }
assert_raise(ActiveResource::InvalidRequestError) { @conn.head("/people/2.xml") }
end
def test_client_nonce_is_not_nil
assert_not_nil ActiveResource::Connection.new("http://david:test123@localhost").send(:client_nonce)
end
protected
@@ -119,4 +229,16 @@ class AuthorizationTest < Test::Unit::TestCase
@conn.__send__(:handle_response, Response.new(code))
end
end
def blank_digest_auth_header(uri, response)
%Q(Digest username="david", realm="", qop="", uri="#{uri}", nonce="", nc="0", cnonce="i-am-a-client-nonce", opaque="", response="#{response}")
end
def request_digest_auth_header(uri, response)
%Q(Digest username="david", realm="RailsTestApp", qop="auth", uri="#{uri}", nonce="#{@nonce}", nc="0", cnonce="i-am-a-client-nonce", opaque="ef6dfb078ba22298d366f99567814ffb", response="#{response}")
end
def response_digest_auth_header
%Q(Digest realm="RailsTestApp", qop="auth", algorithm=MD5, nonce="#{@nonce}", opaque="ef6dfb078ba22298d366f99567814ffb")
end
end

View File

@@ -163,6 +163,12 @@ class BaseTest < Test::Unit::TestCase
assert_equal('test123', Forum.connection.password)
end
def test_should_accept_setting_auth_type
Forum.auth_type = :digest
assert_equal(:digest, Forum.auth_type)
assert_equal(:digest, Forum.connection.auth_type)
end
def test_should_accept_setting_timeout
Forum.timeout = 5
assert_equal(5, Forum.timeout)

View File

@@ -225,6 +225,21 @@ class ConnectionTest < Test::Unit::TestCase
assert_raise(ActiveResource::SSLError) { @conn.get('/people/1.xml') }
end
def test_auth_type_can_be_string
@conn.auth_type = 'digest'
assert_equal(:digest, @conn.auth_type)
end
def test_auth_type_defaults_to_basic
@conn.auth_type = nil
assert_equal(:basic, @conn.auth_type)
end
def test_auth_type_ignores_nonsensical_values
@conn.auth_type = :wibble
assert_equal(:basic, @conn.auth_type)
end
protected
def assert_response_raises(klass, code)
assert_raise(klass, "Expected response code #{code} to raise #{klass}") do

View File

@@ -7,6 +7,8 @@ module ActiveSupport
autoload :Callbacks, 'active_support/callbacks'
autoload :Concern, 'active_support/concern'
autoload :ConcurrentHash, 'active_support/concurrent_hash'
autoload :Configurable, 'active_support/configurable'
autoload :DependencyModule, 'active_support/dependency_module'
autoload :DeprecatedCallbacks, 'active_support/deprecated_callbacks'
autoload :Deprecation, 'active_support/deprecation'
autoload :Gzip, 'active_support/gzip'

View File

@@ -0,0 +1,35 @@
require "active_support/concern"
module ActiveSupport
module Configurable
extend ActiveSupport::Concern
module ClassMethods
def get_config
module_parts = name.split("::")
modules = [Object]
module_parts.each {|name| modules.push modules.last.const_get(name) }
modules.reverse_each do |mod|
return mod.const_get(:DEFAULT_CONFIG) if const_defined?(:DEFAULT_CONFIG)
end
{}
end
def config
self.config = get_config unless @config
@config
end
def config=(hash)
@config = ActiveSupport::OrderedOptions.new
hash.each do |key, value|
@config[key] = value
end
end
end
def config
self.class.config
end
end
end

View File

@@ -1,5 +1,3 @@
require 'active_support/core_ext/load_error'
module ActiveSupport
module Testing
class ProxyTestResult

View File

@@ -3,10 +3,8 @@ module ActiveSupport
module SetupAndTeardown
def self.included(base)
base.class_eval do
extend ClassMethods
include ActiveSupport::Callbacks
define_callbacks :test
include ActiveSupport::DeprecatedCallbacks
define_callbacks :setup, :teardown
if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
include ForMiniTest
@@ -16,33 +14,20 @@ module ActiveSupport
end
end
module ClassMethods
def setup(*args, &block)
set_callback(:test, :before, *args, &block)
end
def teardown(*args, &block)
set_callback(:test, :after, *args, &block)
end
def wrap(*args, &block)
set_callback(:test, :around, *args, &block)
end
end
module ForMiniTest
def run(runner)
result = '.'
begin
run_callbacks :test do
begin
result = super
rescue Exception => e
result = runner.puke(self.class, self.name, e)
end
end
run_callbacks :setup
result = super
rescue Exception => e
result = runner.puke(self.class, self.name, e)
ensure
begin
run_callbacks :teardown, :enumerator => :reverse_each
rescue Exception => e
result = runner.puke(self.class, self.name, e)
end
end
result
end
@@ -70,27 +55,27 @@ module ActiveSupport
@_result = result
begin
begin
run_callbacks :test do
begin
setup
__send__(@method_name)
mocha_verify(assertion_counter) if using_mocha
rescue Mocha::ExpectationError => e
add_failure(e.message, e.backtrace)
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue Exception => e
raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
add_error(e)
ensure
teardown
end
end
run_callbacks :setup
setup
__send__(@method_name)
mocha_verify(assertion_counter) if using_mocha
rescue Mocha::ExpectationError => e
add_failure(e.message, e.backtrace)
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue Exception => e
raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
add_error(e)
ensure
begin
teardown
run_callbacks :teardown, :enumerator => :reverse_each
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
rescue Exception => e
raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
add_error(e)
end
end
ensure
mocha_teardown if using_mocha

View File

@@ -13,8 +13,6 @@ module ActiveSupport
data = StringIO.new(data || '')
end
LibXML::XML.default_keep_blanks = false
char = data.getc
if char.nil?
{}
@@ -44,9 +42,9 @@ module LibXML #:nodoc:
# hash::
# Hash to merge the converted element into.
def to_hash(hash={})
if text?
raise LibXML::XML::Error if content.length >= LIB_XML_LIMIT
hash[CONTENT_ROOT] = content
if text? || cdata?
raise LibXML::XML::Error if hash[CONTENT_ROOT].to_s.length + content.length >= LIB_XML_LIMIT
hash[CONTENT_ROOT] = hash[CONTENT_ROOT].to_s + content
else
sub_hash = insert_name_into_hash(hash, name)
attributes_to_hash(sub_hash)
@@ -88,6 +86,11 @@ module LibXML #:nodoc:
# Hash to merge the children into.
def children_to_hash(hash={})
each { |child| child.to_hash(hash) }
if hash.length > 1 && hash[CONTENT_ROOT].blank?
hash.delete(CONTENT_ROOT)
end
attributes_to_hash(hash)
hash
end

View File

@@ -95,3 +95,55 @@ end
class AlsoDoingNothingTest < ActiveSupport::TestCase
end
# Setup and teardown callbacks.
class SetupAndTeardownTest < ActiveSupport::TestCase
setup :reset_callback_record, :foo
teardown :foo, :sentinel, :foo
def test_inherited_setup_callbacks
assert_equal [:reset_callback_record, :foo], self.class.setup_callback_chain.map(&:method)
assert_equal [:foo], @called_back
assert_equal [:foo, :sentinel, :foo], self.class.teardown_callback_chain.map(&:method)
end
def setup
end
def teardown
end
protected
def reset_callback_record
@called_back = []
end
def foo
@called_back << :foo
end
def sentinel
assert_equal [:foo, :foo], @called_back
end
end
class SubclassSetupAndTeardownTest < SetupAndTeardownTest
setup :bar
teardown :bar
def test_inherited_setup_callbacks
assert_equal [:reset_callback_record, :foo, :bar], self.class.setup_callback_chain.map(&:method)
assert_equal [:foo, :bar], @called_back
assert_equal [:foo, :sentinel, :foo, :bar], self.class.teardown_callback_chain.map(&:method)
end
protected
def bar
@called_back << :bar
end
def sentinel
assert_equal [:foo, :bar, :bar, :foo], @called_back
end
end

View File

@@ -0,0 +1,194 @@
require 'abstract_unit'
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/conversions'
begin
require 'libxml'
rescue LoadError
# Skip libxml tests
else
class LibxmlEngineTest < Test::Unit::TestCase
include ActiveSupport
def setup
@default_backend = XmlMini.backend
XmlMini.backend = 'LibXML'
LibXML::XML::Error.set_handler(&lambda { |error| }) #silence libxml, exceptions will do
end
def teardown
XmlMini.backend = @default_backend
end
def test_exception_thrown_on_expansion_attack
assert_raise LibXML::XML::Error do
attack_xml = %{<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE member [
<!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
<!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
<!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;">
<!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;">
<!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;">
<!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;">
<!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
]>
<member>
&a;
</member>
}
Hash.from_xml(attack_xml)
end
end
def test_setting_libxml_as_backend
XmlMini.backend = 'LibXML'
assert_equal XmlMini_LibXML, 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)
<blog>
<posts type="array">
<post>a post</post>
<post>another post</post>
</posts>
</blog>
eoxml
end
def test_one_node_document_as_hash
assert_equal_rexml(<<-eoxml)
<products/>
eoxml
end
def test_one_node_with_attributes_document_as_hash
assert_equal_rexml(<<-eoxml)
<products foo="bar"/>
eoxml
end
def test_products_node_with_book_node_as_hash
assert_equal_rexml(<<-eoxml)
<products>
<book name="awesome" id="12345" />
</products>
eoxml
end
def test_products_node_with_two_book_nodes_as_hash
assert_equal_rexml(<<-eoxml)
<products>
<book name="awesome" id="12345" />
<book name="america" id="67890" />
</products>
eoxml
end
def test_single_node_with_content_as_hash
assert_equal_rexml(<<-eoxml)
<products>
hello world
</products>
eoxml
end
def test_children_with_children
assert_equal_rexml(<<-eoxml)
<root>
<products>
<book name="america" id="67890" />
</products>
</root>
eoxml
end
def test_children_with_text
assert_equal_rexml(<<-eoxml)
<root>
<products>
hello everyone
</products>
</root>
eoxml
end
def test_children_with_non_adjacent_text
assert_equal_rexml(<<-eoxml)
<root>
good
<products>
hello everyone
</products>
morning
</root>
eoxml
end
def test_parse_from_io
io = StringIO.new(<<-eoxml)
<root>
good
<products>
hello everyone
</products>
morning
</root>
eoxml
XmlMini.parse(io)
end
def test_children_with_simple_cdata
assert_equal_rexml(<<-eoxml)
<root>
<products>
<![CDATA[cdatablock]]>
</products>
</root>
eoxml
end
def test_children_with_multiple_cdata
assert_equal_rexml(<<-eoxml)
<root>
<products>
<![CDATA[cdatablock1]]><![CDATA[cdatablock2]]>
</products>
</root>
eoxml
end
def test_children_with_text_and_cdata
assert_equal_rexml(<<-eoxml)
<root>
<products>
hello <![CDATA[cdatablock]]>
morning
</products>
</root>
eoxml
end
def test_children_with_blank_text
assert_equal_rexml(<<-eoxml)
<root>
<products> </products>
</root>
eoxml
end
private
def assert_equal_rexml(xml)
hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) }
assert_equal(hash, XmlMini.parse(xml))
end
end
end

View File

@@ -28,6 +28,7 @@ task :default => :test
task :test do
dir = ENV["TEST_DIR"] || "**"
Dir["test/#{dir}/*_test.rb"].all? do |file|
next true if file.include?("fixtures")
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
system(ruby, '-Itest', "-I#{File.dirname(__FILE__)}/../activesupport/lib", file)
end or raise "Failures"

View File

@@ -18,6 +18,10 @@ module Rails
@plugin_loader ||= config.plugin_loader.new(self)
end
def root
config.root
end
def routes
ActionController::Routing::Routes
end
@@ -102,7 +106,7 @@ module Rails
# Create tmp directories
initializer :ensure_tmp_directories_exist do
%w(cache pids sessions sockets).each do |dir_to_make|
FileUtils.mkdir_p(File.join(config.root_path, 'tmp', dir_to_make))
FileUtils.mkdir_p(File.join(config.root, 'tmp', dir_to_make))
end
end
@@ -346,7 +350,7 @@ module Rails
# Loads all plugins in <tt>config.plugin_paths</tt>. <tt>plugin_paths</tt>
# defaults to <tt>vendor/plugins</tt> but may also be set to a list of
# paths, such as
# config.plugin_paths = ["#{RAILS_ROOT}/lib/plugins", "#{RAILS_ROOT}/vendor/plugins"]
# config.plugin_paths = ["#{config.root}/lib/plugins", "#{config.root}/vendor/plugins"]
#
# In the default implementation, as each plugin discovered in <tt>plugin_paths</tt> is initialized:
# * its +lib+ directory, if present, is added to the load path (immediately after the applications lib directory)
@@ -394,7 +398,7 @@ module Rails
initializer :load_application_initializers do
if config.gems_dependencies_loaded
Dir["#{configuration.root_path}/config/initializers/**/*.rb"].sort.each do |initializer|
Dir["#{configuration.root}/config/initializers/**/*.rb"].sort.each do |initializer|
load(initializer)
end
end

View File

@@ -1,8 +1,11 @@
require 'rails/plugin/loader'
require 'rails/plugin/locator'
module Rails
class Configuration
attr_accessor :cache_classes, :load_paths, :eager_load_paths, :framework_paths,
attr_accessor :cache_classes, :load_paths,
:load_once_paths, :gems_dependencies_loaded, :after_initialize_blocks,
:frameworks, :framework_root_path, :root_path, :plugin_paths, :plugins,
:frameworks, :framework_root_path, :root, :plugin_paths, :plugins,
:plugin_loader, :plugin_locators, :gems, :loaded_plugins, :reload_plugins,
:i18n, :gems, :whiny_nils, :consider_all_requests_local,
:action_controller, :active_record, :action_view, :active_support,
@@ -13,31 +16,13 @@ module Rails
:eager_load_paths, :dependency_loading, :paths, :serve_static_assets
def initialize
set_root_path!
@framework_paths = []
@load_once_paths = []
@after_initialize_blocks = []
@loaded_plugins = []
@dependency_loading = true
@eager_load_paths = default_eager_load_paths
@load_paths = default_load_paths
@plugin_paths = default_plugin_paths
@frameworks = default_frameworks
@plugin_loader = default_plugin_loader
@plugin_locators = default_plugin_locators
@gems = default_gems
@i18n = default_i18n
@log_path = default_log_path
@log_level = default_log_level
@cache_store = default_cache_store
@view_path = default_view_path
@controller_paths = default_controller_paths
@routes_configuration_file = default_routes_configuration_file
@database_configuration_file = default_database_configuration_file
@serve_static_assets = default_serve_static_assets
@serve_static_assets = true
for framework in default_frameworks
for framework in frameworks
self.send("#{framework}=", Rails::OrderedOptions.new)
end
self.active_support = Rails::OrderedOptions.new
@@ -47,38 +32,60 @@ module Rails
@after_initialize_blocks << blk if blk
end
def set_root_path!
raise 'RAILS_ROOT is not set' unless defined?(RAILS_ROOT)
raise 'RAILS_ROOT is not a directory' unless File.directory?(RAILS_ROOT)
self.root_path =
# Pathname is incompatible with Windows, but Windows doesn't have
# real symlinks so File.expand_path is safe.
if RUBY_PLATFORM =~ /(:?mswin|mingw)/
File.expand_path(RAILS_ROOT)
# Otherwise use Pathname#realpath which respects symlinks.
def root
@root ||= begin
if defined?(RAILS_ROOT)
root = RAILS_ROOT
else
Pathname.new(RAILS_ROOT).realpath.to_s
call_stack = caller.map { |p| p.split(':').first }
root_path = call_stack.detect { |p| p !~ %r[railties/lib/rails] }
root_path = File.dirname(root_path)
while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/config.ru")
parent = File.dirname(root_path)
root_path = parent != root_path && parent
end
Object.class_eval("RAILS_ROOT = ''")
root = File.exist?("#{root_path}/config.ru") ? root_path : Dir.pwd
end
@paths = Rails::Application::Root.new(root_path)
@paths.app "app", :load_path => true
@paths.app.metals "app/metal", :eager_load => true
@paths.app.models "app/models", :eager_load => true
@paths.app.controllers "app/controllers", builtin_directories, :eager_load => true
@paths.app.helpers "app/helpers", :eager_load => true
@paths.app.services "app/services", :load_path => true
@paths.lib "lib", :load_path => true
@paths.vendor "vendor", :load_path => true
@paths.vendor.plugins "vendor/plugins"
@paths.tmp "tmp"
@paths.tmp.cache "tmp/cache"
@paths.config "config"
@paths.config.locales "config/locales"
@paths.config.environments "config/environments", :glob => "#{RAILS_ENV}.rb"
root = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ?
Pathname.new(root).expand_path.to_s :
Pathname.new(root).realpath.to_s
RAILS_ROOT.replace root_path
# TODO: Remove RAILS_ROOT
RAILS_ROOT.replace(root)
root
end
end
def root=(root)
Object.class_eval("RAILS_ROOT = ''") unless defined?(RAILS_ROOT)
RAILS_ROOT.replace(root)
@root = root
end
def paths
@paths ||= begin
paths = Rails::Application::Root.new(root)
paths.app "app", :load_path => true
paths.app.metals "app/metal", :eager_load => true
paths.app.models "app/models", :eager_load => true
paths.app.controllers "app/controllers", builtin_directories, :eager_load => true
paths.app.helpers "app/helpers", :eager_load => true
paths.app.services "app/services", :load_path => true
paths.lib "lib", :load_path => true
paths.vendor "vendor", :load_path => true
paths.vendor.plugins "vendor/plugins"
paths.tmp "tmp"
paths.tmp.cache "tmp/cache"
paths.config "config"
paths.config.locales "config/locales"
paths.config.environments "config/environments", :glob => "#{RAILS_ENV}.rb"
paths
end
end
# Enable threaded mode. Allows concurrent requests to controller actions and
@@ -105,7 +112,7 @@ module Rails
end
def framework_root_path
defined?(::RAILS_FRAMEWORK_ROOT) ? ::RAILS_FRAMEWORK_ROOT : "#{root_path}/vendor/rails"
defined?(::RAILS_FRAMEWORK_ROOT) ? ::RAILS_FRAMEWORK_ROOT : "#{root}/vendor/rails"
end
def middleware
@@ -121,63 +128,69 @@ module Rails
YAML::load(ERB.new(IO.read(database_configuration_file)).result)
end
def default_routes_configuration_file
File.join(root_path, 'config', 'routes.rb')
def routes_configuration_file
@routes_configuration_file ||= File.join(root, 'config', 'routes.rb')
end
def default_controller_paths
paths = [File.join(root_path, 'app', 'controllers')]
paths.concat builtin_directories
paths
end
def default_cache_store
if File.exist?("#{root_path}/tmp/cache/")
[ :file_store, "#{root_path}/tmp/cache/" ]
else
:memory_store
def controller_paths
@controller_paths ||= begin
paths = [File.join(root, 'app', 'controllers')]
paths.concat builtin_directories
paths
end
end
def default_database_configuration_file
File.join(root_path, 'config', 'database.yml')
def cache_store
@cache_store ||= begin
if File.exist?("#{root}/tmp/cache/")
[ :file_store, "#{root}/tmp/cache/" ]
else
:memory_store
end
end
end
def default_view_path
File.join(root_path, 'app', 'views')
def database_configuration_file
@database_configuration_file ||= File.join(root, 'config', 'database.yml')
end
def default_eager_load_paths
%w(
def view_path
@view_path ||= File.join(root, 'app', 'views')
end
def eager_load_paths
@eager_load_paths ||= %w(
app/metal
app/models
app/controllers
app/helpers
).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
).map { |dir| "#{root}/#{dir}" }.select { |dir| File.directory?(dir) }
end
def default_load_paths
paths = []
def load_paths
@load_paths ||= begin
paths = []
# Add the old mock paths only if the directories exists
paths.concat(Dir["#{root_path}/test/mocks/#{RAILS_ENV}"]) if File.exists?("#{root_path}/test/mocks/#{RAILS_ENV}")
# Add the old mock paths only if the directories exists
paths.concat(Dir["#{root}/test/mocks/#{RAILS_ENV}"]) if File.exists?("#{root}/test/mocks/#{RAILS_ENV}")
# Add the app's controller directory
paths.concat(Dir["#{root_path}/app/controllers/"])
# Add the app's controller directory
paths.concat(Dir["#{root}/app/controllers/"])
# Followed by the standard includes.
paths.concat %w(
app
app/metal
app/models
app/controllers
app/helpers
app/services
lib
vendor
).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
# Followed by the standard includes.
paths.concat %w(
app
app/metal
app/models
app/controllers
app/helpers
app/services
lib
vendor
).map { |dir| "#{root}/#{dir}" }.select { |dir| File.directory?(dir) }
paths.concat builtin_directories
paths.concat builtin_directories
end
end
def builtin_directories
@@ -185,48 +198,48 @@ module Rails
(RAILS_ENV == 'development') ? Dir["#{RAILTIES_PATH}/builtin/*/"] : []
end
def default_log_path
File.join(root_path, 'log', "#{RAILS_ENV}.log")
def log_path
@log_path ||= File.join(root, 'log', "#{RAILS_ENV}.log")
end
def default_log_level
RAILS_ENV == 'production' ? :info : :debug
def log_level
@log_level ||= RAILS_ENV == 'production' ? :info : :debug
end
def default_frameworks
[ :active_record, :action_controller, :action_view, :action_mailer, :active_resource ]
def frameworks
@frameworks ||= [ :active_record, :action_controller, :action_view, :action_mailer, :active_resource ]
end
def default_plugin_paths
["#{root_path}/vendor/plugins"]
def plugin_paths
@plugin_paths ||= ["#{root}/vendor/plugins"]
end
def default_plugin_loader
require 'rails/plugin/loader'
Plugin::Loader
end
def default_plugin_locators
require 'rails/plugin/locator'
locators = []
locators << Plugin::GemLocator if defined? Gem
locators << Plugin::FileSystemLocator
end
def default_i18n
i18n = Rails::OrderedOptions.new
i18n.load_path = []
if File.exist?(File.join(RAILS_ROOT, 'config', 'locales'))
i18n.load_path << Dir[File.join(RAILS_ROOT, 'config', 'locales', '*.{rb,yml}')]
i18n.load_path.flatten!
def plugin_loader
@plugin_loader ||= begin
Plugin::Loader
end
i18n
end
def default_serve_static_assets
true
def plugin_locators
@plugin_locators ||= begin
locators = []
locators << Plugin::GemLocator if defined? Gem
locators << Plugin::FileSystemLocator
end
end
def i18n
@i18n ||= begin
i18n = Rails::OrderedOptions.new
i18n.load_path = []
if File.exist?(File.join(root, 'config', 'locales'))
i18n.load_path << Dir[File.join(root, 'config', 'locales', '*.{rb,yml}')]
i18n.load_path.flatten!
end
i18n
end
end
# Adds a single Gem dependency to the rails application. By default, it will require
@@ -241,15 +254,15 @@ module Rails
#
# config.gem 'qrp', :version => '0.4.1', :lib => false
def gem(name, options = {})
@gems << Rails::GemDependency.new(name, options)
gems << Rails::GemDependency.new(name, options)
end
def default_gems
[]
def gems
@gems ||= []
end
def environment_path
"#{root_path}/config/environments/#{RAILS_ENV}.rb"
"#{root}/config/environments/#{RAILS_ENV}.rb"
end
def reload_plugins?

View File

@@ -54,6 +54,7 @@ module Rails::Generators
copy_file "Rakefile"
copy_file "README"
copy_file "config.ru"
template "Gemfile"
end
def create_app_files

View File

@@ -0,0 +1,10 @@
# Gemfile is where you list all of your application's dependencies
#
gem "rails", "<%= Rails::VERSION::STRING %>"
#
# Bundling edge rails:
# gem "rails", "<%= Rails::VERSION::STRING %>", :git => "git://github.com/rails/rails.git"
# You can list more dependencies here
# gem "nokogiri"
# gem "merb" # ;)

View File

@@ -19,14 +19,14 @@ module Rails
class Root
include PathParent
attr_reader :path
attr_accessor :path
def initialize(path)
raise unless path.is_a?(String)
raise if path.is_a?(Array)
@children = {}
# TODO: Move logic from set_root_path initializer
@path = File.expand_path(path)
@path = path
@root = self
@all_paths = []
end
@@ -123,8 +123,10 @@ module Rails
end
def paths
raise "You need to set a path root" unless @root.path
@paths.map do |path|
path.index('/') == 0 ? path : File.join(@root.path, path)
path.index('/') == 0 ? path : File.expand_path(File.join(@root.path, path))
end
end

View File

@@ -0,0 +1,49 @@
require "isolation/abstract_unit"
module ApplicationTests
class InitializerTest < Test::Unit::TestCase
include ActiveSupport::Testing::Isolation
def setup
build_app
boot_rails
Object.send(:remove_const, :RAILS_ROOT)
end
test "the application root is set correctly" do
require "#{app_path}/config/environment"
assert_equal app_path, Rails.application.root
end
test "the application root can be set" do
FileUtils.mkdir_p("#{app_path}/hello")
add_to_config <<-RUBY
config.frameworks = []
config.root = '#{app_path}/hello'
RUBY
require "#{app_path}/config/environment"
assert_equal "#{app_path}/hello", Rails.application.root
end
test "the application root is detected as where config.ru is located" do
add_to_config <<-RUBY
config.frameworks = []
RUBY
FileUtils.mv "#{app_path}/config.ru", "#{app_path}/config/config.ru"
require "#{app_path}/config/environment"
assert_equal "#{app_path}/config", Rails.application.root
end
test "the application root is Dir.pwd if there is no config.ru" do
File.delete("#{app_path}/config.ru")
add_to_config <<-RUBY
config.frameworks = []
RUBY
Dir.chdir("#{app_path}/app") do
require "#{app_path}/config/environment"
assert_equal "#{app_path}/app", Rails.application.root
end
end
end
end

View File

@@ -5,9 +5,9 @@ module ApplicationTests
include ActiveSupport::Testing::Isolation
def setup
require "rails/generators"
build_app
boot_rails
require "rails/generators"
end
test "generators default values" do

View File

@@ -8,12 +8,17 @@ else
RAILS_ROOT = fixtures
end
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../activemodel/lib"
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../activerecord/lib"
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../actionpack/lib"
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib"
# TODO: Fix this RAILS_ENV stuff
RAILS_ENV = 'test'
require "rails/core"
require 'rails/generators'
require 'rubygems'
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../activerecord/lib"
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../../actionpack/lib"
require 'active_record'
require 'action_dispatch'

View File

@@ -25,8 +25,10 @@ module TestHelpers
module Paths
module_function
TMP_PATH = File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. tmp]))
def tmp_path(*args)
File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. tmp] + args))
File.join(TMP_PATH, *args)
end
def app_path(*args)
@@ -88,10 +90,14 @@ module TestHelpers
end
end
add_to_config 'config.action_controller.session = { :key => "_myapp_session", :secret => "bac838a849c1d5c4de2e6a50af826079" }'
end
def add_to_config(str)
environment = File.read("#{app_path}/config/environment.rb")
if environment =~ /(\n\s*end\s*)\Z/
File.open("#{app_path}/config/environment.rb", 'w') do |f|
f.puts $` + %'\nconfig.action_controller.session = { :key => "_myapp_session", :secret => "bac838a849c1d5c4de2e6a50af826079" }\n' + $1
f.puts $` + "\n#{str}\n" + $1
end
end
end

View File

@@ -12,11 +12,32 @@ class PathsTest < ActiveSupport::TestCase
assert_equal "/fiz/baz", root.path
end
test "the paths object can be initialized with nil" do
assert_nothing_raised do
Rails::Application::Root.new(nil)
end
end
test "a paths object initialized with nil can be updated" do
root = Rails::Application::Root.new(nil)
root.app = "app"
root.path = "/root"
assert_equal ["/root/app"], root.app.to_a
end
test "creating a root level path" do
@root.app = "/foo/bar"
assert_equal ["/foo/bar"], @root.app.to_a
end
test "raises exception if root path never set" do
root = Rails::Application::Root.new(nil)
root.app = "app"
assert_raises RuntimeError do
root.app.to_a
end
end
test "creating a root level path without assignment" do
@root.app "/foo/bar"
assert_equal ["/foo/bar"], @root.app.to_a