aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Kemper <jeremy@bitsweat.net>2009-10-14 17:24:17 -0700
committerJeremy Kemper <jeremy@bitsweat.net>2009-10-14 17:24:17 -0700
commit5ad0e315e41d73aa5dcd8973f38dd99a5384e5ac (patch)
treec56a5b7dbafee9719b1aabc2fc5da87f17ebf554
parent4cbd3f050b5db2a1164b1071753d72bea7234ff0 (diff)
parentbf9819f73d74e19052b7b8a7a9885972a27e8876 (diff)
downloadrails-5ad0e315e41d73aa5dcd8973f38dd99a5384e5ac.tar.gz
rails-5ad0e315e41d73aa5dcd8973f38dd99a5384e5ac.tar.bz2
rails-5ad0e315e41d73aa5dcd8973f38dd99a5384e5ac.zip
Merge branch 'master' into arel
-rw-r--r--actionpack/lib/action_controller/metal/cookies.rb3
-rw-r--r--actionpack/lib/action_controller/metal/redirector.rb2
-rw-r--r--actionpack/lib/action_controller/testing/process.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb8
-rw-r--r--actionpack/lib/action_view/base.rb2
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb28
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb4
-rw-r--r--actionpack/lib/action_view/safe_buffer.rb2
-rw-r--r--actionpack/test/controller/cookie_test.rb7
-rw-r--r--actionpack/test/controller/integration_test.rb18
-rw-r--r--actionpack/test/controller/test_test.rb17
-rw-r--r--actionpack/test/dispatch/session/test_session_test.rb4
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb10
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb3
-rw-r--r--actionpack/test/template/url_helper_test.rb24
-rw-r--r--activeresource/lib/active_resource/base.rb12
-rw-r--r--activeresource/lib/active_resource/connection.rb90
-rw-r--r--activeresource/test/abstract_unit.rb1
-rw-r--r--activeresource/test/cases/authorization_test.rb170
-rw-r--r--activeresource/test/cases/base_test.rb6
-rw-r--r--activeresource/test/connection_test.rb15
-rw-r--r--activesupport/lib/active_support/autoload.rb2
-rw-r--r--activesupport/lib/active_support/configurable.rb35
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb2
-rw-r--r--activesupport/lib/active_support/testing/setup_and_teardown.rb67
-rw-r--r--activesupport/lib/active_support/xml_mini/libxml.rb13
-rw-r--r--activesupport/test/test_test.rb52
-rw-r--r--activesupport/test/xml_mini/libxml_engine_test.rb194
-rw-r--r--railties/Rakefile1
-rw-r--r--railties/lib/rails/application.rb10
-rw-r--r--railties/lib/rails/configuration.rb261
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile10
-rw-r--r--railties/lib/rails/paths.rb12
-rw-r--r--railties/test/application/configuration_test.rb49
-rw-r--r--railties/test/application/generators_test.rb2
-rw-r--r--railties/test/generators/generators_test_helper.rb9
-rw-r--r--railties/test/isolation/abstract_unit.rb10
-rw-r--r--railties/test/paths_test.rb21
39 files changed, 933 insertions, 246 deletions
diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb
index d4806623c3..c328db8beb 100644
--- a/actionpack/lib/action_controller/metal/cookies.rb
+++ b/actionpack/lib/action_controller/metal/cookies.rb
@@ -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
diff --git a/actionpack/lib/action_controller/metal/redirector.rb b/actionpack/lib/action_controller/metal/redirector.rb
index f79fd54acd..b55f5e7bfc 100644
--- a/actionpack/lib/action_controller/metal/redirector.rb
+++ b/actionpack/lib/action_controller/metal/redirector.rb
@@ -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
diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb
index bbc7f3c8f9..323cce6a2f 100644
--- a/actionpack/lib/action_controller/testing/process.rb
+++ b/actionpack/lib/action_controller/testing/process.rb
@@ -35,7 +35,7 @@ module ActionController #:nodoc:
end
def cookies
- @response.cookies
+ @request.cookies.merge(@response.cookies)
end
def redirect_to_url
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 2c4a3a356d..58ebe94a5b 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -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
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 31e9c5ef9d..5f28ba6ccb 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -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
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index faa7f2e2e9..15b70ecff5 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -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)
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index e651bc17a9..5b136d4f54 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -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
diff --git a/actionpack/lib/action_view/safe_buffer.rb b/actionpack/lib/action_view/safe_buffer.rb
index 8ba9cd80d6..09f44ab26f 100644
--- a/actionpack/lib/action_view/safe_buffer.rb
+++ b/actionpack/lib/action_view/safe_buffer.rb
@@ -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
diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/controller/cookie_test.rb
index 7199da3441..b429cbf0e6 100644
--- a/actionpack/test/controller/cookie_test.rb
+++ b/actionpack/test/controller/cookie_test.rb
@@ -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"
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 508364d0b5..fe95fb5750 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -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.
diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb
index 73870a56bb..375878b755 100644
--- a/actionpack/test/controller/test_test.rb
+++ b/actionpack/test/controller/test_test.rb
@@ -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
diff --git a/actionpack/test/dispatch/session/test_session_test.rb b/actionpack/test/dispatch/session/test_session_test.rb
index 0ff93f1c5d..c8dc4ab461 100644
--- a/actionpack/test/dispatch/session/test_session_test.rb
+++ b/actionpack/test/dispatch/session/test_session_test.rb
@@ -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
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index d94135b04b..57802ebf42 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -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"
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index d64b9492e2..47462b1237 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -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
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 7f6ebc56b7..cec53e479c 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -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")
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index b21d8db613..ae627c365d 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -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
diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb
index 9d551f04e7..98cb1a932b 100644
--- a/activeresource/lib/active_resource/connection.rb
+++ b/activeresource/lib/active_resource/connection.rb
@@ -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
+
+ 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
- # Sets authorization header
- def authorization_header
- (@user || @password ? { 'Authorization' => 'Basic ' + ["#{@user}:#{ @password}"].pack('m').delete("\r\n") } : {})
+ 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
diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb
index 05efac79cf..6a33040243 100644
--- a/activeresource/test/abstract_unit.rb
+++ b/activeresource/test/abstract_unit.rb
@@ -1,5 +1,6 @@
require 'rubygems'
require 'test/unit'
+require 'active_support'
require 'active_support/test_case'
$:.unshift "#{File.dirname(__FILE__)}/../lib"
diff --git a/activeresource/test/cases/authorization_test.rb b/activeresource/test/cases/authorization_test.rb
index ca25f437e3..1a7c9ec8a4 100644
--- a/activeresource/test/cases/authorization_test.rb
+++ b/activeresource/test/cases/authorization_test.rb
@@ -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
diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb
index 1593e25595..1d3f7891ec 100644
--- a/activeresource/test/cases/base_test.rb
+++ b/activeresource/test/cases/base_test.rb
@@ -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)
diff --git a/activeresource/test/connection_test.rb b/activeresource/test/connection_test.rb
index 2a3e04272a..a2744d7531 100644
--- a/activeresource/test/connection_test.rb
+++ b/activeresource/test/connection_test.rb
@@ -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
diff --git a/activesupport/lib/active_support/autoload.rb b/activesupport/lib/active_support/autoload.rb
index 47a17687bf..f3a68b482f 100644
--- a/activesupport/lib/active_support/autoload.rb
+++ b/activesupport/lib/active_support/autoload.rb
@@ -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'
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
new file mode 100644
index 0000000000..890f465ce1
--- /dev/null
+++ b/activesupport/lib/active_support/configurable.rb
@@ -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 \ No newline at end of file
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index bec303f6ab..c75b59c284 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/load_error'
-
module ActiveSupport
module Testing
class ProxyTestResult
diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb
index b738ef334c..7952eb50c3 100644
--- a/activesupport/lib/active_support/testing/setup_and_teardown.rb
+++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb
@@ -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
diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb
index 2ae22c35fb..0f7ba1918b 100644
--- a/activesupport/lib/active_support/xml_mini/libxml.rb
+++ b/activesupport/lib/active_support/xml_mini/libxml.rb
@@ -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
diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb
index 7a45dab60b..5cbffb81fc 100644
--- a/activesupport/test/test_test.rb
+++ b/activesupport/test/test_test.rb
@@ -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
diff --git a/activesupport/test/xml_mini/libxml_engine_test.rb b/activesupport/test/xml_mini/libxml_engine_test.rb
new file mode 100644
index 0000000000..900c8052d6
--- /dev/null
+++ b/activesupport/test/xml_mini/libxml_engine_test.rb
@@ -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
diff --git a/railties/Rakefile b/railties/Rakefile
index 23f9603b65..0ba5ca10c2 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -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"
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index d54120f850..a0e5d6a5a5 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -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
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 4a70a4800e..322590f108 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -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
-
- for framework in default_frameworks
+ @serve_static_assets = true
+
+ 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)
+ def root
+ @root ||= begin
+ if defined?(RAILS_ROOT)
+ root = RAILS_ROOT
+ else
+ 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)
- 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)
+ 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
- # Otherwise use Pathname#realpath which respects symlinks.
- else
- Pathname.new(RAILS_ROOT).realpath.to_s
+ 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"
-
- RAILS_ROOT.replace root_path
+ root = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ?
+ Pathname.new(root).expand_path.to_s :
+ Pathname.new(root).realpath.to_s
+
+ # 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
+ def controller_paths
+ @controller_paths ||= begin
+ paths = [File.join(root, 'app', 'controllers')]
+ paths.concat builtin_directories
+ paths
+ end
end
- def default_cache_store
- if File.exist?("#{root_path}/tmp/cache/")
- [ :file_store, "#{root_path}/tmp/cache/" ]
- else
- :memory_store
+ 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_database_configuration_file
- File.join(root_path, 'config', 'database.yml')
+ def database_configuration_file
+ @database_configuration_file ||= File.join(root, 'config', 'database.yml')
end
- def default_view_path
- File.join(root_path, 'app', 'views')
+ def view_path
+ @view_path ||= File.join(root, 'app', 'views')
end
- def default_eager_load_paths
- %w(
+ 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 = []
-
- # 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 app's controller directory
- paths.concat(Dir["#{root_path}/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) }
-
- paths.concat builtin_directories
+ def load_paths
+ @load_paths ||= begin
+ paths = []
+
+ # 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}/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}/#{dir}" }.select { |dir| File.directory?(dir) }
+
+ 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
+ def plugin_loader
+ @plugin_loader ||= begin
+ Plugin::Loader
+ end
end
- def default_plugin_locators
- require 'rails/plugin/locator'
- locators = []
- locators << Plugin::GemLocator if defined? Gem
- locators << Plugin::FileSystemLocator
+ def plugin_locators
+ @plugin_locators ||= begin
+ locators = []
+ locators << Plugin::GemLocator if defined? Gem
+ locators << Plugin::FileSystemLocator
+ end
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!
- end
+ def i18n
+ @i18n ||= begin
+ i18n = Rails::OrderedOptions.new
+ i18n.load_path = []
- i18n
- end
+ if File.exist?(File.join(root, 'config', 'locales'))
+ i18n.load_path << Dir[File.join(root, 'config', 'locales', '*.{rb,yml}')]
+ i18n.load_path.flatten!
+ end
- def default_serve_static_assets
- true
+ 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?
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 78b4b057ae..bd7af25bd5 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -54,6 +54,7 @@ module Rails::Generators
copy_file "Rakefile"
copy_file "README"
copy_file "config.ru"
+ template "Gemfile"
end
def create_app_files
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
new file mode 100644
index 0000000000..bcbaa432b7
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -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" # ;) \ No newline at end of file
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 3899b744b0..0f24106353 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -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
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
new file mode 100644
index 0000000000..d90582d3db
--- /dev/null
+++ b/railties/test/application/configuration_test.rb
@@ -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 \ No newline at end of file
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index 0d6eb4147a..0edb29483d 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -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
diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb
index d917812383..7599bda8a2 100644
--- a/railties/test/generators/generators_test_helper.rb
+++ b/railties/test/generators/generators_test_helper.rb
@@ -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'
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index f83e0151a4..245577e8c0 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -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
diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb
index d50882110f..c724799d64 100644
--- a/railties/test/paths_test.rb
+++ b/railties/test/paths_test.rb
@@ -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