aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2008-05-11 23:50:08 +0100
committerPratik Naik <pratiknaik@gmail.com>2008-05-11 23:50:08 +0100
commitcb50a2880759f311148abda55ab60be772b8aa51 (patch)
tree3f8f851f332c0976349de9dc2b4a722946b4cc5e
parent35634feb474cc55fbc95edeffe98cec241d45f23 (diff)
parent9a137506a1267ec5938fcec4d2ff135f15037459 (diff)
downloadrails-cb50a2880759f311148abda55ab60be772b8aa51.tar.gz
rails-cb50a2880759f311148abda55ab60be772b8aa51.tar.bz2
rails-cb50a2880759f311148abda55ab60be772b8aa51.zip
Merge commit 'mainstream/master'
-rw-r--r--actionpack/CHANGELOG4
-rw-r--r--actionpack/lib/action_controller/flash.rb7
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb14
-rw-r--r--actionpack/lib/action_controller/request_forgery_protection.rb6
-rw-r--r--actionpack/lib/action_controller/routing/segments.rb9
-rw-r--r--actionpack/lib/action_controller/session_management.rb5
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb5
-rw-r--r--actionpack/lib/action_view/partials.rb4
-rw-r--r--actionpack/test/activerecord/render_partial_with_record_identification_test.rb189
-rw-r--r--actionpack/test/controller/record_identifier_test.rb36
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb24
-rw-r--r--actionpack/test/controller/routing_test.rb12
-rw-r--r--actionpack/test/controller/session_management_test.rb22
-rw-r--r--actionpack/test/controller/test_test.rb10
-rw-r--r--activerecord/CHANGELOG4
-rw-r--r--activerecord/lib/active_record/association_preload.rb23
-rwxr-xr-xactiverecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb10
-rw-r--r--activerecord/lib/active_record/named_scope.rb12
-rwxr-xr-xactiverecord/lib/active_record/validations.rb2
-rw-r--r--activerecord/test/cases/associations/eager_test.rb17
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb8
-rw-r--r--activerecord/test/cases/migration_test.rb6
-rw-r--r--activerecord/test/cases/named_scope_test.rb6
-rwxr-xr-xactiverecord/test/cases/validations_test.rb8
-rw-r--r--activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb7
-rw-r--r--activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb7
-rw-r--r--activerecord/test/models/author.rb4
-rw-r--r--activerecord/test/models/guid.rb2
-rw-r--r--activerecord/test/schema/schema.rb4
-rw-r--r--activesupport/CHANGELOG2
-rw-r--r--activesupport/lib/active_support/json.rb29
-rw-r--r--railties/Rakefile2
33 files changed, 393 insertions, 109 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index baba4ae5ed..2caaa40bf6 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,9 @@
*SVN*
+* Fixed that forgery protection can be used without session tracking (Peter Jones) [#139]
+
+* Added session(:on) to turn session management back on in a controller subclass if the superclass turned it off (Peter Jones) [#136]
+
* InstanceTag#default_time_from_options with hash args uses Time.current as default; respects hash settings when time falls in system local spring DST gap [Geoff Buesing]
* select_date defaults to Time.zone.today when config.time_zone is set [Geoff Buesing]
diff --git a/actionpack/lib/action_controller/flash.rb b/actionpack/lib/action_controller/flash.rb
index 692168f230..0148fb5c04 100644
--- a/actionpack/lib/action_controller/flash.rb
+++ b/actionpack/lib/action_controller/flash.rb
@@ -28,7 +28,6 @@ module ActionController #:nodoc:
base.class_eval do
include InstanceMethods
alias_method_chain :assign_shortcuts, :flash
- alias_method_chain :process_cleanup, :flash
alias_method_chain :reset_session, :flash
end
end
@@ -166,11 +165,7 @@ module ActionController #:nodoc:
def assign_shortcuts_with_flash(request, response) #:nodoc:
assign_shortcuts_without_flash(request, response)
flash(:refresh)
- end
-
- def process_cleanup_with_flash
- flash.sweep if @_session
- process_cleanup_without_flash
+ flash.sweep if @_session && !component_request?
end
end
end
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index 22dbc8bbc5..643ff7e5f4 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -33,11 +33,17 @@ module ActionController
# Returns plural/singular for a record or class. Example:
#
- # partial_path(post) # => "posts/post"
- # partial_path(Person) # => "people/person"
- def partial_path(record_or_class)
+ # partial_path(post) # => "posts/post"
+ # partial_path(Person) # => "people/person"
+ # partial_path(Person, "admin/games") # => "admin/people/person"
+ def partial_path(record_or_class, controller_path = nil)
klass = class_from_record_or_class(record_or_class)
- "#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
+
+ if controller_path && controller_path.include?("/")
+ "#{File.dirname(controller_path)}/#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
+ else
+ "#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
+ end
end
# The DOM class convention is to use the singular form of an object or class. Examples:
diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/request_forgery_protection.rb
index 5daf14eb30..139e91ecf9 100644
--- a/actionpack/lib/action_controller/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/request_forgery_protection.rb
@@ -105,12 +105,12 @@ module ActionController #:nodoc:
# Sets the token value for the current session. Pass a <tt>:secret</tt> option
# in +protect_from_forgery+ to add a custom salt to the hash.
def form_authenticity_token
- @form_authenticity_token ||= if request_forgery_protection_options[:secret]
+ @form_authenticity_token ||= if !session.respond_to?(:session_id)
+ raise InvalidAuthenticityToken, "Request Forgery Protection requires a valid session. Use #allow_forgery_protection to disable it, or use a valid session."
+ elsif request_forgery_protection_options[:secret]
authenticity_token_from_session_id
elsif session.respond_to?(:dbman) && session.dbman.respond_to?(:generate_digest)
authenticity_token_from_cookie_session
- elsif session.nil?
- raise InvalidAuthenticityToken, "Request Forgery Protection requires a valid session. Use #allow_forgery_protection to disable it, or use a valid session."
else
raise InvalidAuthenticityToken, "No :secret given to the #protect_from_forgery call. Set that or use a session store capable of generating its own keys (Cookie Session Store)."
end
diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb
index 24ea8c7f2d..b142d18b47 100644
--- a/actionpack/lib/action_controller/routing/segments.rb
+++ b/actionpack/lib/action_controller/routing/segments.rb
@@ -244,11 +244,12 @@ module ActionController
end
class PathSegment < DynamicSegment #:nodoc:
- RESERVED_PCHAR = "#{Segment::RESERVED_PCHAR}/"
- UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze
-
def interpolation_chunk(value_code = "#{local_name}")
- "\#{URI.escape(#{value_code}.to_s, ActionController::Routing::PathSegment::UNSAFE_PCHAR)}"
+ "\#{#{value_code}}"
+ end
+
+ def extract_value
+ "#{local_name} = hash[:#{key}] && hash[:#{key}].collect { |path_component| URI.escape(path_component, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}"
end
def default
diff --git a/actionpack/lib/action_controller/session_management.rb b/actionpack/lib/action_controller/session_management.rb
index 8680104420..80a3ddd2c5 100644
--- a/actionpack/lib/action_controller/session_management.rb
+++ b/actionpack/lib/action_controller/session_management.rb
@@ -69,11 +69,16 @@ module ActionController #:nodoc:
# session :off,
# :if => Proc.new { |req| !(req.format.html? || req.format.js?) }
#
+ # # turn the session back on, useful when it was turned off in the
+ # # application controller, and you need it on in another controller
+ # session :on
+ #
# All session options described for ActionController::Base.process_cgi
# are valid arguments.
def session(*args)
options = args.extract_options!
+ options[:disabled] = false if args.delete(:on)
options[:disabled] = true if !args.empty?
options[:only] = [*options[:only]].map { |o| o.to_s } if options[:only]
options[:except] = [*options[:except]].map { |o| o.to_s } if options[:except]
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index 3129ff414e..6c0a7ec25c 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -48,6 +48,11 @@ module ActionView
# config.action_view.sanitized_allowed_attributes = 'id', 'class', 'style'
# end
#
+ # Please note that sanitizing user-provided text does not guarantee that the
+ # resulting markup is valid (conforming to a document type) or even well-formed.
+ # The output may still contain e.g. unescaped '<', '>', '&' characters and
+ # confuse browsers.
+ #
def sanitize(html, options = {})
self.class.white_list_sanitizer.sanitize(html, options)
end
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
index a708ecb3fb..6b294be6bd 100644
--- a/actionpack/lib/action_view/partials.rb
+++ b/actionpack/lib/action_view/partials.rb
@@ -119,7 +119,7 @@ module ActionView
""
end
else
- render_partial(ActionController::RecordIdentifier.partial_path(partial_path), partial_path, local_assigns)
+ render_partial(ActionController::RecordIdentifier.partial_path(partial_path, controller.class.controller_path), partial_path, local_assigns)
end
end
@@ -147,7 +147,7 @@ module ActionView
templates = Hash.new
i = 0
collection.map do |element|
- partial_path = ActionController::RecordIdentifier.partial_path(element)
+ partial_path = ActionController::RecordIdentifier.partial_path(element, controller.class.controller_path)
template = templates[partial_path] ||= ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns)
template.counter = i
i += 1
diff --git a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
index 32b26206c3..ed10e72953 100644
--- a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
+++ b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb
@@ -1,49 +1,49 @@
require 'active_record_unit'
-class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
- fixtures :developers, :projects, :developers_projects, :topics, :replies, :companies, :mascots
+class RenderPartialWithRecordIdentificationController < ActionController::Base
+ def render_with_has_many_and_belongs_to_association
+ @developer = Developer.find(1)
+ render :partial => @developer.projects
+ end
- class RenderPartialWithRecordIdentificationController < ActionController::Base
- def render_with_has_many_and_belongs_to_association
- @developer = Developer.find(1)
- render :partial => @developer.projects
- end
-
- def render_with_has_many_association
- @topic = Topic.find(1)
- render :partial => @topic.replies
- end
-
- def render_with_named_scope
- render :partial => Reply.base
- end
-
- def render_with_has_many_through_association
- @developer = Developer.find(:first)
- render :partial => @developer.topics
- end
-
- def render_with_has_one_association
- @company = Company.find(1)
- render :partial => @company.mascot
- end
-
- def render_with_belongs_to_association
- @reply = Reply.find(1)
- render :partial => @reply.topic
- end
-
- def render_with_record
- @developer = Developer.find(:first)
- render :partial => @developer
- end
-
- def render_with_record_collection
- @developers = Developer.find(:all)
- render :partial => @developers
- end
+ def render_with_has_many_association
+ @topic = Topic.find(1)
+ render :partial => @topic.replies
+ end
+
+ def render_with_named_scope
+ render :partial => Reply.base
+ end
+
+ def render_with_has_many_through_association
+ @developer = Developer.find(:first)
+ render :partial => @developer.topics
+ end
+
+ def render_with_has_one_association
+ @company = Company.find(1)
+ render :partial => @company.mascot
+ end
+
+ def render_with_belongs_to_association
+ @reply = Reply.find(1)
+ render :partial => @reply.topic
end
- RenderPartialWithRecordIdentificationController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
+
+ def render_with_record
+ @developer = Developer.find(:first)
+ render :partial => @developer
+ end
+
+ def render_with_record_collection
+ @developers = Developer.find(:all)
+ render :partial => @developers
+ end
+end
+RenderPartialWithRecordIdentificationController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
+
+class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
+ fixtures :developers, :projects, :developers_projects, :topics, :replies, :companies, :mascots
def setup
@controller = RenderPartialWithRecordIdentificationController.new
@@ -84,3 +84,108 @@ class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase
assert_equal mascot.name, @response.body
end
end
+
+class RenderPartialWithRecordIdentificationController < ActionController::Base
+ def render_with_has_many_and_belongs_to_association
+ @developer = Developer.find(1)
+ render :partial => @developer.projects
+ end
+
+ def render_with_has_many_association
+ @topic = Topic.find(1)
+ render :partial => @topic.replies
+ end
+
+ def render_with_has_many_through_association
+ @developer = Developer.find(:first)
+ render :partial => @developer.topics
+ end
+
+ def render_with_belongs_to_association
+ @reply = Reply.find(1)
+ render :partial => @reply.topic
+ end
+
+ def render_with_record
+ @developer = Developer.find(:first)
+ render :partial => @developer
+ end
+
+ def render_with_record_collection
+ @developers = Developer.find(:all)
+ render :partial => @developers
+ end
+end
+RenderPartialWithRecordIdentificationController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
+
+class Game < Struct.new(:name, :id)
+ def to_param
+ id.to_s
+ end
+end
+
+module Fun
+ class NestedController < ActionController::Base
+ def render_with_record_in_nested_controller
+ render :partial => Game.new("Pong")
+ end
+
+ def render_with_record_collection_in_nested_controller
+ render :partial => [ Game.new("Pong"), Game.new("Tank") ]
+ end
+ end
+ NestedController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
+
+ module Serious
+ class NestedDeeperController < ActionController::Base
+ def render_with_record_in_deeper_nested_controller
+ render :partial => Game.new("Chess")
+ end
+
+ def render_with_record_collection_in_deeper_nested_controller
+ render :partial => [ Game.new("Chess"), Game.new("Sudoku"), Game.new("Solitaire") ]
+ end
+ end
+ NestedDeeperController.view_paths = [ File.dirname(__FILE__) + "/../fixtures/" ]
+ end
+end
+
+class RenderPartialWithRecordIdentificationAndNestedControllersTest < ActiveRecordTestCase
+ def setup
+ @controller = Fun::NestedController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ super
+ end
+
+ def test_render_with_record_in_nested_controller
+ get :render_with_record_in_nested_controller
+ assert_template 'fun/games/_game'
+ end
+
+ def test_render_with_record_collection_in_nested_controller
+ get :render_with_record_collection_in_nested_controller
+ assert_template 'fun/games/_game'
+ end
+
+end
+
+class RenderPartialWithRecordIdentificationAndNestedDeeperControllersTest < ActiveRecordTestCase
+ def setup
+ @controller = Fun::Serious::NestedDeeperController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ super
+ end
+
+ def test_render_with_record_in_deeper_nested_controller
+ get :render_with_record_in_deeper_nested_controller
+ assert_template 'fun/serious/games/_game'
+ end
+
+ def test_render_with_record_collection_in_deeper_nested_controller
+ get :render_with_record_collection_in_deeper_nested_controller
+ assert_template 'fun/serious/games/_game'
+ end
+
+end \ No newline at end of file
diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb
index def8613215..12c1eaea69 100644
--- a/actionpack/test/controller/record_identifier_test.rb
+++ b/actionpack/test/controller/record_identifier_test.rb
@@ -57,6 +57,18 @@ class RecordIdentifierTest < Test::Unit::TestCase
assert_equal expected, partial_path(Comment)
end
+ def test_partial_path_with_namespaced_controller_path
+ expected = "admin/#{@plural}/#{@singular}"
+ assert_equal expected, partial_path(@record, "admin/posts")
+ assert_equal expected, partial_path(@klass, "admin/posts")
+ end
+
+ def test_partial_path_with_not_namespaced_controller_path
+ expected = "#{@plural}/#{@singular}"
+ assert_equal expected, partial_path(@record, "posts")
+ assert_equal expected, partial_path(@klass, "posts")
+ end
+
def test_dom_class
assert_equal @singular, dom_class(@record)
end
@@ -100,4 +112,28 @@ class NestedRecordIdentifierTest < RecordIdentifierTest
assert_equal expected, partial_path(@record)
assert_equal expected, partial_path(Comment::Nested)
end
+
+ def test_partial_path_with_namespaced_controller_path
+ expected = "admin/comment/nesteds/nested"
+ assert_equal expected, partial_path(@record, "admin/posts")
+ assert_equal expected, partial_path(@klass, "admin/posts")
+ end
+
+ def test_partial_path_with_deeper_namespaced_controller_path
+ expected = "deeper/admin/comment/nesteds/nested"
+ assert_equal expected, partial_path(@record, "deeper/admin/posts")
+ assert_equal expected, partial_path(@klass, "deeper/admin/posts")
+ end
+
+ def test_partial_path_with_even_deeper_namespaced_controller_path
+ expected = "even/more/deeper/admin/comment/nesteds/nested"
+ assert_equal expected, partial_path(@record, "even/more/deeper/admin/posts")
+ assert_equal expected, partial_path(@klass, "even/more/deeper/admin/posts")
+ end
+
+ def test_partial_path_with_not_namespaced_controller_path
+ expected = "comment/nesteds/nested"
+ assert_equal expected, partial_path(@record, "posts")
+ assert_equal expected, partial_path(@klass, "posts")
+ end
end
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index d0c3c6e224..7022713e30 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -50,6 +50,14 @@ class CsrfCookieMonsterController < ActionController::Base
protect_from_forgery :only => :index
end
+# sessions are turned off
+class SessionOffController < ActionController::Base
+ protect_from_forgery :secret => 'foobar'
+ session :off
+ def rescue_action(e) raise e end
+ include RequestForgeryProtectionActions
+end
+
class FreeCookieController < CsrfCookieMonsterController
self.allow_forgery_protection = false
@@ -224,3 +232,19 @@ class FreeCookieControllerTest < Test::Unit::TestCase
end
end
end
+
+class SessionOffControllerTest < Test::Unit::TestCase
+ def setup
+ @controller = SessionOffController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @token = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('SHA1'), 'abc', '123')
+ end
+
+ def test_should_raise_correct_exception
+ @request.session = {} # session(:off) doesn't appear to work with controller tests
+ assert_raises(ActionController::InvalidAuthenticityToken) do
+ post :index, :authenticity_token => @token
+ end
+ end
+end
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 640afd58f8..b28f7bcdff 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -25,7 +25,7 @@ class UriReservedCharactersRoutingTest < Test::Unit::TestCase
ActionController::Routing.use_controllers! ['controller']
@set = ActionController::Routing::RouteSet.new
@set.draw do |map|
- map.connect ':controller/:action/:variable'
+ map.connect ':controller/:action/:variable/*additional'
end
safe, unsafe = %w(: @ & = + $ , ;), %w(^ / ? # [ ])
@@ -36,17 +36,19 @@ class UriReservedCharactersRoutingTest < Test::Unit::TestCase
end
def test_route_generation_escapes_unsafe_path_characters
- assert_equal "/contr#{@segment}oller/act#{@escaped}ion/var#{@escaped}iable",
+ assert_equal "/contr#{@segment}oller/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2",
@set.generate(:controller => "contr#{@segment}oller",
:action => "act#{@segment}ion",
- :variable => "var#{@segment}iable")
+ :variable => "var#{@segment}iable",
+ :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"])
end
def test_route_recognition_unescapes_path_components
options = { :controller => "controller",
:action => "act#{@segment}ion",
- :variable => "var#{@segment}iable" }
- assert_equal options, @set.recognize_path("/controller/act#{@escaped}ion/var#{@escaped}iable")
+ :variable => "var#{@segment}iable",
+ :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"] }
+ assert_equal options, @set.recognize_path("/controller/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2")
end
end
diff --git a/actionpack/test/controller/session_management_test.rb b/actionpack/test/controller/session_management_test.rb
index 495a9153f8..592b0b549d 100644
--- a/actionpack/test/controller/session_management_test.rb
+++ b/actionpack/test/controller/session_management_test.rb
@@ -13,6 +13,19 @@ class SessionManagementTest < Test::Unit::TestCase
end
end
+ class SessionOffOnController < ActionController::Base
+ session :off
+ session :on, :only => :tell
+
+ def show
+ render :text => "done"
+ end
+
+ def tell
+ render :text => "done"
+ end
+ end
+
class TestController < ActionController::Base
session :off, :only => :show
session :session_secure => true, :except => :show
@@ -100,6 +113,15 @@ class SessionManagementTest < Test::Unit::TestCase
assert_equal false, @request.session_options
end
+ def test_session_off_then_on_globally
+ @controller = SessionOffOnController.new
+ get :show
+ assert_equal false, @request.session_options
+ get :tell
+ assert_instance_of Hash, @request.session_options
+ assert_equal false, @request.session_options[:disabled]
+ end
+
def test_session_off_conditionally
@controller = TestController.new
get :show
diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb
index 04cc2a20d8..ba6c7f4299 100644
--- a/actionpack/test/controller/test_test.rb
+++ b/actionpack/test/controller/test_test.rb
@@ -12,6 +12,11 @@ class TestTest < Test::Unit::TestCase
render :text => 'ignore me'
end
+ def set_flash_now
+ flash.now["test_now"] = ">#{flash["test_now"]}<"
+ render :text => 'ignore me'
+ end
+
def set_session
session['string'] = 'A wonder'
session[:symbol] = 'it works'
@@ -145,6 +150,11 @@ XML
assert_equal '>value<', flash['test']
end
+ def test_process_with_flash_now
+ process :set_flash_now, nil, nil, { "test_now" => "value_now" }
+ assert_equal '>value_now<', flash['test_now']
+ end
+
def test_process_with_session
process :set_session
assert_equal 'A wonder', session['string'], "A value stored in the session should be available by string key"
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 264ea79d4a..4a130bf5c0 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,9 @@
*SVN*
+* Ensure hm:t preloading honours reflection options. Resolves #137. [Frederick Cheung]
+
+* Added protection against duplicate migration names (Aslak Hellesøy) [#112]
+
* Base#instantiate_time_object: eliminate check for Time.zone, since we can assume this is set if time_zone_aware_attributes is set to true [Geoff Buesing]
* Time zone aware attribute methods use Time.zone.parse instead of #to_time for String arguments, so that offset information in String is respected. Resolves #105. [Scott Fleckenstein, Geoff Buesing]
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index da4ebdef51..a3d1f12b03 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -31,12 +31,12 @@ module ActiveRecord
private
def preload_one_association(records, association, preload_options={})
- reflection = reflections[association]
- raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection
-
- # Not all records have the same class, so group then preload.
- records.group_by(&:class).each do |klass, records|
- reflection = klass.reflections[association]
+ class_to_reflection = {}
+ # Not all records have the same class, so group then preload
+ # group on the reflection itself so that if various subclass share the same association then we do not split them
+ # unncessarily
+ records.group_by {|record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, records|
+ raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection
send("preload_#{reflection.macro}_association", records, reflection, preload_options)
end
end
@@ -143,7 +143,8 @@ module ActiveRecord
through_primary_key = through_reflection.primary_key_name
unless through_records.empty?
source = reflection.source_reflection.name
- through_records.first.class.preload_associations(through_records, source)
+ #add conditions from reflection!
+ through_records.first.class.preload_associations(through_records, source, reflection.options)
through_records.each do |through_record|
add_preloaded_records_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
reflection.name, through_record.send(source))
@@ -251,12 +252,12 @@ module ActiveRecord
conditions << append_conditions(options, preload_options)
reflection.klass.find(:all,
- :select => (options[:select] || "#{table_name}.*"),
- :include => options[:include],
+ :select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
+ :include => preload_options[:include] || options[:include],
:conditions => [conditions, ids],
:joins => options[:joins],
- :group => options[:group],
- :order => options[:order])
+ :group => preload_options[:group] || options[:group],
+ :order => preload_options[:order] || options[:order])
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 74299bd572..7999eec55d 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1910,6 +1910,8 @@ module ActiveRecord #:nodoc:
# { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
# "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
def sanitize_sql_for_conditions(condition)
+ return nil if condition.blank?
+
case condition
when Array; sanitize_sql_array(condition)
when Hash; sanitize_sql_hash_for_conditions(condition)
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index af4fb6e83c..5cc9f4e197 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -8,6 +8,12 @@ module ActiveRecord
end
end
+ class DuplicateMigrationNameError < ActiveRecordError#:nodoc:
+ def initialize(name)
+ super("Multiple migrations have the name #{name}")
+ end
+ end
+
class UnknownMigrationVersionError < ActiveRecordError #:nodoc:
def initialize(version)
super("No migration with version number #{version}")
@@ -440,6 +446,10 @@ module ActiveRecord
if klasses.detect { |m| m.version == version }
raise DuplicateMigrationVersionError.new(version)
end
+
+ if klasses.detect { |m| m.name == name.camelize }
+ raise DuplicateMigrationNameError.new(name.camelize)
+ end
load(file)
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 81b99f8e96..d43ebefc3b 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -71,6 +71,18 @@ module ActiveRecord
# end
# end
#
+ #
+ # For testing complex named scopes, you can examine the scoping options using the
+ # <tt>proxy_options</tt> method on the proxy itself.
+ #
+ # class Shirt < ActiveRecord::Base
+ # named_scope :colored, lambda { |color|
+ # { :conditions => { :color => color } }
+ # }
+ # end
+ #
+ # expected_options = { :conditions => { :colored => 'red' } }
+ # assert_equal expected_options, Shirt.colored('red').proxy_options
def named_scope(name, options = {}, &block)
scopes[name] = lambda do |parent_scope, *args|
Scope.new(parent_scope, case options
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index d25e8cd0da..b3a75121ed 100755
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -640,7 +640,7 @@ module ActiveRecord
results = finder_class.with_exclusive_scope do
connection.select_all(
construct_finder_sql(
- :select => "#{attr_name}",
+ :select => "#{connection.quote_column_name(attr_name)}",
:from => "#{finder_class.quoted_table_name}",
:conditions => [condition_sql, *condition_params]
)
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 67b57ceb42..3a3358e39b 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -275,6 +275,17 @@ class EagerAssociationTest < ActiveRecord::TestCase
Author.find(:first, :order => 'authors.id').hello_post_comments.sort_by(&:id)
end
+ def test_eager_with_has_many_through_join_model_with_conditions_on_top_level
+ assert_equal comments(:more_greetings), Author.find(authors(:david).id, :include => :comments_with_order_and_conditions).comments_with_order_and_conditions.first
+ end
+
+ def test_eager_with_has_many_through_join_model_with_include
+ author_comments = Author.find(authors(:david).id, :include => :comments_with_include).comments_with_include.to_a
+ assert_no_queries do
+ author_comments.first.post.title
+ end
+ end
+
def test_eager_with_has_many_and_limit
posts = Post.find(:all, :order => 'posts.id asc', :include => [ :author, :comments ], :limit => 2)
assert_equal 2, posts.size
@@ -592,4 +603,10 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
end
end
+
+ def test_load_with_sti_sharing_association
+ assert_queries(2) do #should not do 1 query per subclass
+ Comment.find :all, :include => :post
+ end
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 7b97afe42c..9e26e2ad58 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -48,6 +48,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 2, Firm.find(:first).clients.length
end
+ def test_find_with_blank_conditions
+ [[], {}, nil, ""].each do |blank|
+ assert_equal 2, Firm.find(:first).clients.find(:all, :conditions => blank).size
+ end
+ end
+
def test_find_many_with_merged_options
assert_equal 1, companies(:first_firm).limited_clients.size
assert_equal 1, companies(:first_firm).limited_clients.find(:all).size
@@ -851,4 +857,4 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert ! firm.clients.include?(client)
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 6be31b5f86..527856b4c0 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -984,6 +984,12 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
+ def test_migrator_with_duplicate_names
+ assert_raises(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate_names", nil)
+ end
+ end
+
def test_migrator_with_missing_version_numbers
assert_raise(ActiveRecord::UnknownMigrationVersionError) do
ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500)
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index e99448c23e..30c074c9d8 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -112,4 +112,10 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal Topic.find(:all, scope), Topic.scoped(scope)
end
+
+ def test_proxy_options
+ expected_proxy_options = { :conditions => { :approved => true } }
+ assert_equal expected_proxy_options, Topic.approved.proxy_options
+ end
+
end
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index e3ca8660ac..a4d9da4806 100755
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -5,6 +5,7 @@ require 'models/reply'
require 'models/person'
require 'models/developer'
require 'models/warehouse_thing'
+require 'models/guid'
# The following methods in Topic are used in test_conditional_validation_*
class Topic
@@ -493,6 +494,13 @@ class ValidationsTest < ActiveRecord::TestCase
end
end
+ def test_validate_uniqueness_with_columns_which_are_sql_keywords
+ Guid.validates_uniqueness_of :key
+ g = Guid.new
+ g.key = "foo"
+ assert_nothing_raised { !g.valid? }
+ end
+
def test_validate_straight_inheritance_uniqueness
w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
assert w1.valid?, "Saving w1"
diff --git a/activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb b/activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb
new file mode 100644
index 0000000000..5fe5089e18
--- /dev/null
+++ b/activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb
@@ -0,0 +1,7 @@
+class Chunky < ActiveRecord::Migration
+ def self.up
+ end
+
+ def self.down
+ end
+end
diff --git a/activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb b/activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb
new file mode 100644
index 0000000000..5fe5089e18
--- /dev/null
+++ b/activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb
@@ -0,0 +1,7 @@
+class Chunky < ActiveRecord::Migration
+ def self.up
+ end
+
+ def self.down
+ end
+end
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 2918139f7f..f63af27403 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -17,6 +17,10 @@ class Author < ActiveRecord::Base
end
has_many :comments, :through => :posts
has_many :comments_containing_the_letter_e, :through => :posts, :source => :comments
+ has_many :comments_with_order_and_conditions, :through => :posts, :source => :comments, :order => 'comments.body', :conditions => "comments.body like 'Thank%'"
+ has_many :comments_with_include, :through => :posts, :source => :comments, :include => :post
+
+
has_many :comments_desc, :through => :posts, :source => :comments, :order => 'comments.id DESC'
has_many :limited_comments, :through => :posts, :source => :comments, :limit => 1
has_many :funky_comments, :through => :posts, :source => :comments
diff --git a/activerecord/test/models/guid.rb b/activerecord/test/models/guid.rb
new file mode 100644
index 0000000000..9208dc28fa
--- /dev/null
+++ b/activerecord/test/models/guid.rb
@@ -0,0 +1,2 @@
+class Guid < ActiveRecord::Base
+end \ No newline at end of file
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 818237f076..423929fd55 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -403,6 +403,10 @@ ActiveRecord::Schema.define do
create_table(t, :force => true) { }
end
+ create_table :guids, :force => true do |t|
+ t.column :key, :string
+ end
+
except 'SQLite' do
# fk_test_has_fk should be before fk_test_has_pk
create_table :fk_test_has_fk, :force => true do |t|
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index f72825731e..185aff9088 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Remove unused JSON::RESERVED_WORDS, JSON.valid_identifier? and JSON.reserved_word? methods. Resolves #164. [Cheah Chu Yeow]
+
* Adding Date.current, which returns Time.zone.today if config.time_zone is set; otherwise returns Date.today [Geoff Buesing]
* TimeWithZone: date part getter methods (#year #mon #day etc) are defined on class; no longer relying on method_missing [Geoff Buesing]
diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb
index 914cf4f8fe..bbda2c9fa3 100644
--- a/activesupport/lib/active_support/json.rb
+++ b/activesupport/lib/active_support/json.rb
@@ -1,5 +1,3 @@
-
-
module ActiveSupport
# If true, use ISO 8601 format for dates and times. Otherwise, fall back to the ActiveSupport legacy format.
mattr_accessor :use_standard_json_time_format
@@ -19,33 +17,6 @@ module ActiveSupport
@escape_html_entities_in_json = value
end
end
-
- module JSON
- RESERVED_WORDS = %w(
- abstract delete goto private transient
- boolean do if protected try
- break double implements public typeof
- byte else import return var
- case enum in short void
- catch export instanceof static volatile
- char extends int super while
- class final interface switch with
- const finally long synchronized
- continue float native this
- debugger for new throw
- default function package throws
- ) #:nodoc:
-
- class << self
- def valid_identifier?(key) #:nodoc:
- key.to_s =~ /^[[:alpha:]_$][[:alnum:]_$]*$/ && !reserved_word?(key)
- end
-
- def reserved_word?(key) #:nodoc:
- RESERVED_WORDS.include?(key.to_s)
- end
- end
- end
end
require 'active_support/json/encoding'
diff --git a/railties/Rakefile b/railties/Rakefile
index ef673c234e..4eaa1bef63 100644
--- a/railties/Rakefile
+++ b/railties/Rakefile
@@ -255,7 +255,7 @@ task :generate_rails_framework_doc do
end
task :generate_app_doc do
- File.cp "doc/README_FOR_APP", "#{PKG_DESTINATION}/doc/README_FOR_APP"
+ cp "doc/README_FOR_APP", "#{PKG_DESTINATION}/doc/README_FOR_APP"
system %{cd #{PKG_DESTINATION}; rake doc:app}
end