aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCody Fauser <cody@jadedpixel.com>2008-05-14 09:11:35 -0400
committerCody Fauser <cody@jadedpixel.com>2008-05-14 09:11:35 -0400
commit9226b3e8355e7b6aa3be62fff56117d75c408831 (patch)
tree2952484a50583e4c438d0abb37ff549e6c9811dc
parentb8c46c86f0b785f6afc3c58eeb1b347543f5901e (diff)
parent9546ee299952c86329c4854f9b3776382c0575ff (diff)
downloadrails-9226b3e8355e7b6aa3be62fff56117d75c408831.tar.gz
rails-9226b3e8355e7b6aa3be62fff56117d75c408831.tar.bz2
rails-9226b3e8355e7b6aa3be62fff56117d75c408831.zip
Merge branch 'master' of git@github.com:lifo/docrails
-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/CHANGELOG6
-rw-r--r--activerecord/lib/active_record/association_preload.rb23
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb40
-rwxr-xr-xactiverecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb90
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb20
-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/core_ext/module/attr_internal.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/range/conversions.rb5
-rw-r--r--activesupport/lib/active_support/inflector.rb7
-rw-r--r--activesupport/lib/active_support/json.rb29
-rw-r--r--railties/README16
-rw-r--r--railties/Rakefile2
40 files changed, 497 insertions, 191 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 597b876f22..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]
@@ -8,7 +12,7 @@
change_table :videos do |t|
t.timestamps # adds created_at, updated_at
- t.belongs_to :goat # add goat_id integer
+ t.belongs_to :goat # adds goat_id integer
t.string :name, :email, :limit => 20 # adds name and email both with a 20 char limit
t.remove :name, :email # removes the name and email columns
end
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/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 2db27226f2..c2604894a8 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -16,16 +16,20 @@ module ActiveRecord
# Declare and check for suffixed attribute methods.
module ClassMethods
- # Declare a method available for all attributes with the given suffix.
- # Uses method_missing and respond_to? to rewrite the method
+ # Declares a method available for all attributes with the given suffix.
+ # Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method
+ #
# #{attr}#{suffix}(*args, &block)
+ #
# to
+ #
# attribute#{suffix}(#{attr}, *args, &block)
#
- # An attribute#{suffix} instance method must exist and accept at least
- # the attr argument.
+ # An <tt>attribute#{suffix}</tt> instance method must exist and accept at least
+ # the +attr+ argument.
#
# For example:
+ #
# class Person < ActiveRecord::Base
# attribute_method_suffix '_changed?'
#
@@ -60,8 +64,8 @@ module ActiveRecord
!generated_methods.empty?
end
- # generates all the attribute related methods for columns in the database
- # accessors, mutators and query methods
+ # Generates all the attribute related methods for columns in the database
+ # accessors, mutators and query methods.
def define_attribute_methods
return if generated_methods?
columns_hash.each do |name, column|
@@ -89,8 +93,9 @@ module ActiveRecord
end
end
- # Check to see if the method is defined in the model or any of its subclasses that also derive from ActiveRecord.
- # Raise DangerousAttributeError if the method is defined by ActiveRecord though.
+ # Checks whether the method is defined in the model or any of its subclasses
+ # that also derive from ActiveRecord. Raises DangerousAttributeError if the
+ # method is defined by Active Record though.
def instance_method_already_implemented?(method_name)
method_name = method_name.to_s
return true if method_name =~ /^id(=$|\?$|$)/
@@ -104,17 +109,19 @@ module ActiveRecord
# +cache_attributes+ allows you to declare which converted attribute values should
# be cached. Usually caching only pays off for attributes with expensive conversion
- # methods, like date columns (e.g. created_at, updated_at).
+ # methods, like time related columns (e.g. +created_at+, +updated_at+).
def cache_attributes(*attribute_names)
attribute_names.each {|attr| cached_attributes << attr.to_s}
end
- # returns the attributes where
+ # Returns the attributes which are cached. By default time related columns
+ # with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
def cached_attributes
@cached_attributes ||=
columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map(&:name).to_set
end
+ # Returns +true+ if the provided attribute is being cached.
def cache_attribute?(attr_name)
cached_attributes.include?(attr_name)
end
@@ -210,14 +217,14 @@ module ActiveRecord
end # ClassMethods
- # Allows access to the object attributes, which are held in the @attributes hash, as though they
+ # Allows access to the object attributes, which are held in the <tt>@attributes</tt> hash, as though they
# were first-class methods. So a Person class with a name attribute can use Person#name and
# Person#name= and never directly use the attributes hash -- except for multiple assigns with
# ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
- # the completed attribute is not nil or 0.
+ # the completed attribute is not +nil+ or 0.
#
# It's also possible to instantiate related objects, so a Client class belonging to the clients
- # table with a master_id foreign key can instantiate master through Client#master.
+ # table with a +master_id+ foreign key can instantiate master through Client#master.
def method_missing(method_id, *args, &block)
method_name = method_id.to_s
@@ -288,7 +295,7 @@ module ActiveRecord
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
- # columns are turned into nil.
+ # columns are turned into +nil+.
def write_attribute(attr_name, value)
attr_name = attr_name.to_s
@attributes_cache.delete(attr_name)
@@ -319,8 +326,9 @@ module ActiveRecord
end
end
- # A Person object with a name attribute can ask person.respond_to?("name"), person.respond_to?("name="), and
- # person.respond_to?("name?") which will all return true.
+ # A Person object with a name attribute can ask <tt>person.respond_to?("name")</tt>,
+ # <tt>person.respond_to?("name=")</tt>, and <tt>person.respond_to?("name?")</tt>
+ # which will all return +true+.
alias :respond_to_without_attributes? :respond_to?
def respond_to?(method, include_priv = false)
method_name = method.to_s
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/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index d73ffc3da6..fdb18b234c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -18,11 +18,11 @@ module ActiveRecord
#
# +name+ is the column's name, as in <tt><b>supplier_id</b> int(11)</tt>.
# +default+ is the type-casted default value, such as <tt>sales_stage varchar(20) default <b>'new'</b></tt>.
- # +sql_type+ is only used to extract the column's length, if necessary. For example, <tt>company_name varchar(<b>60</b>)</tt>.
+ # +sql_type+ is only used to extract the column's length, if necessary. For example, <tt>company_name varchar(<b>60</b>)</tt>.
# +null+ determines if this column allows +NULL+ values.
def initialize(name, default, sql_type = nil, null = true)
@name, @sql_type, @null = name, sql_type, null
- @limit, @precision, @scale = extract_limit(sql_type), extract_precision(sql_type), extract_scale(sql_type)
+ @limit, @precision, @scale = extract_limit(sql_type), extract_precision(sql_type), extract_scale(sql_type)
@type = simplified_type(sql_type)
@default = extract_default(default)
@@ -172,7 +172,7 @@ module ActiveRecord
def new_time(year, mon, mday, hour, min, sec, microsec)
# Treat 0000-00-00 00:00:00 as nil.
return nil if year.nil? || year == 0
-
+
Time.time_with_datetime_fallback(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
end
@@ -250,11 +250,11 @@ module ActiveRecord
end
class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
-
+
def sql_type
base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
end
-
+
def to_sql
column_sql = "#{base.quote_column_name(name)} #{sql_type}"
add_column_options!(column_sql, :null => null, :default => default) unless type.to_sym == :primary_key
@@ -309,39 +309,39 @@ module ActiveRecord
# * <tt>:default</tt> -
# The column's default value. Use nil for NULL.
# * <tt>:null</tt> -
- # Allows or disallows +NULL+ values in the column. This option could
+ # Allows or disallows +NULL+ values in the column. This option could
# have been named <tt>:null_allowed</tt>.
# * <tt>:precision</tt> -
- # Specifies the precision for a <tt>:decimal</tt> column.
+ # Specifies the precision for a <tt>:decimal</tt> column.
# * <tt>:scale</tt> -
- # Specifies the scale for a <tt>:decimal</tt> column.
+ # Specifies the scale for a <tt>:decimal</tt> column.
#
# Please be aware of different RDBMS implementations behavior with
# <tt>:decimal</tt> columns:
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
# <tt>:precision</tt>, and makes no comments about the requirements of
# <tt>:precision</tt>.
- # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
+ # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
# Default is (10,0).
- # * PostgreSQL: <tt>:precision</tt> [1..infinity],
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
# <tt>:scale</tt> [0..infinity]. No default.
- # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
+ # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
# Internal storage as strings. No default.
# * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
# but the maximum supported <tt>:precision</tt> is 16. No default.
- # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
# Default is (38,0).
- # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
+ # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
# Default unknown.
- # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
+ # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
# Default (9,0). Internal types NUMERIC and DECIMAL have different
# storage rules, decimal being better.
- # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
+ # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
# Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
# NUMERIC is 19, and DECIMAL is 38.
- # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
+ # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
# Default (38,0).
- # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
+ # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
# Default (38,0).
# * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
#
@@ -394,7 +394,7 @@ module ActiveRecord
# t.timestamps
# end
#
- # There's a short-hand method for each of the type values declared at the top. And then there's
+ # There's a short-hand method for each of the type values declared at the top. And then there's
# TableDefinition#timestamps that'll add created_at and updated_at as datetimes.
#
# TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
@@ -434,13 +434,13 @@ module ActiveRecord
def #{column_type}(*args)
options = args.extract_options!
column_names = args
-
+
column_names.each { |name| column(name, '#{column_type}', options) }
end
EOV
end
-
- # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
+
+ # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
# <tt>:updated_at</tt> to the table.
def timestamps
column(:created_at, :datetime)
@@ -458,7 +458,7 @@ module ActiveRecord
alias :belongs_to :references
# Returns a String whose contents are the column definitions
- # concatenated together. This string can then be prepended and appended to
+ # concatenated together. This string can then be prepended and appended to
# to generate the final SQL to create the table.
def to_sql
@columns * ', '
@@ -510,15 +510,15 @@ module ActiveRecord
# Adds a new column to the named table.
# See TableDefinition#column for details of the options you can use.
- # ===== Examples
- # ====== Creating a simple columns
+ # ===== Example
+ # ====== Creating a simple column
# t.column(:name, :string)
def column(column_name, type, options = {})
@base.add_column(@table_name, column_name, type, options)
end
- # Adds a new index to the table. +column_name+ can be a single Symbol, or
- # an Array of Symbols. See SchemaStatements#add_index
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
+ # an Array of Symbols. See SchemaStatements#add_index
#
# ===== Examples
# ====== Creating a simple index
@@ -531,8 +531,8 @@ module ActiveRecord
@base.add_index(@table_name, column_name, options)
end
- # Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#timestamps
- # ===== Examples
+ # Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#add_timestamps
+ # ===== Example
# t.timestamps
def timestamps
@base.add_timestamps(@table_name)
@@ -547,7 +547,7 @@ module ActiveRecord
@base.change_column(@table_name, column_name, type, options)
end
- # Sets a new default value for a column. See
+ # Sets a new default value for a column. See SchemaStatements#change_column_default
# ===== Examples
# t.change_default(:qualification, 'new')
# t.change_default(:authorized, 1)
@@ -559,27 +559,27 @@ module ActiveRecord
# ===== Examples
# t.remove(:qualification)
# t.remove(:qualification, :experience)
- # t.removes(:qualification, :experience)
def remove(*column_names)
@base.remove_column(@table_name, column_names)
end
- # Remove the given index from the table.
+ # Removes the given index from the table.
#
- # Remove the suppliers_name_index in the suppliers table.
+ # ===== Examples
+ # ====== Remove the suppliers_name_index in the suppliers table
# t.remove_index :name
- # Remove the index named accounts_branch_id_index in the accounts table.
+ # ====== Remove the index named accounts_branch_id_index in the accounts table
# t.remove_index :column => :branch_id
- # Remove the index named accounts_branch_id_party_id_index in the accounts table.
+ # ====== Remove the index named accounts_branch_id_party_id_index in the accounts table
# t.remove_index :column => [:branch_id, :party_id]
- # Remove the index named by_branch_party in the accounts table.
+ # ====== Remove the index named by_branch_party in the accounts table
# t.remove_index :name => :by_branch_party
def remove_index(options = {})
@base.remove_index(@table_name, options)
end
# Removes the timestamp columns (created_at and updated_at) from the table.
- # ===== Examples
+ # ===== Example
# t.remove_timestamps
def remove_timestamps
@base.remove_timestamps(@table_name)
@@ -592,12 +592,11 @@ module ActiveRecord
@base.rename_column(@table_name, column_name, new_column_name)
end
- # Adds a reference. Optionally adds a +type+ column. <tt>reference</tt>,
- # <tt>references</tt> and <tt>belongs_to</tt> are all acceptable
- # ===== Example
+ # Adds a reference. Optionally adds a +type+ column.
+ # <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
+ # ===== Examples
# t.references(:goat)
# t.references(:goat, :polymorphic => true)
- # t.references(:goat)
# t.belongs_to(:goat)
def references(*args)
options = args.extract_options!
@@ -609,12 +608,11 @@ module ActiveRecord
end
alias :belongs_to :references
- # Adds a reference. Optionally removes a +type+ column. <tt>remove_reference</tt>,
- # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are all acceptable
- # ===== Example
- # t.remove_reference(:goat)
- # t.remove_reference(:goat, :polymorphic => true)
+ # Removes a reference. Optionally removes a +type+ column.
+ # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
+ # ===== Examples
# t.remove_references(:goat)
+ # t.remove_references(:goat, :polymorphic => true)
# t.remove_belongs_to(:goat)
def remove_references(*args)
options = args.extract_options!
@@ -627,7 +625,7 @@ module ActiveRecord
alias :remove_belongs_to :remove_references
# Adds a column or columns of a specified type
- # ===== Example
+ # ===== Examples
# t.string(:goat)
# t.string(:goat, :sheep)
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 1594be40e2..ac24e920fe 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -13,7 +13,7 @@ module ActiveRecord
255
end
- # Truncates a table alias according to the limits of the current adapter.
+ # Truncates a table alias according to the limits of the current adapter.
def table_alias_for(table_name)
table_name[0..table_alias_length-1].gsub(/\./, '_')
end
@@ -152,7 +152,7 @@ module ActiveRecord
# t.remove :company
# end
#
- # ====== Remove a column
+ # ====== Remove several columns
# change_table(:suppliers) do |t|
# t.remove :company_id
# t.remove :width, :height
@@ -168,7 +168,7 @@ module ActiveRecord
def change_table(table_name)
yield Table.new(table_name, self)
end
-
+
# Renames a table.
# ===== Example
# rename_table('octopuses', 'octopi')
@@ -199,7 +199,7 @@ module ActiveRecord
end
end
alias :remove_columns :remove_column
-
+
# Changes the column's definition according to the new options.
# See TableDefinition#column for details of the options you can use.
# ===== Examples
@@ -389,7 +389,7 @@ module ActiveRecord
def distinct(columns, order_by)
"DISTINCT #{columns}"
end
-
+
# ORDER BY clause for the passed order option.
# PostgreSQL overrides this due to its stricter standards compliance.
def add_order_by_for_association_limiting!(sql, options)
@@ -401,17 +401,17 @@ module ActiveRecord
# add_timestamps(:suppliers)
def add_timestamps(table_name)
add_column table_name, :created_at, :datetime
- add_column table_name, :updated_at, :datetime
+ add_column table_name, :updated_at, :datetime
end
-
+
# Removes the timestamp columns (created_at and updated_at) from the table definition.
# ===== Examples
# remove_timestamps(:suppliers)
def remove_timestamps(table_name)
- remove_column table_name, :updated_at
- remove_column table_name, :created_at
+ remove_column table_name, :updated_at
+ remove_column table_name, :created_at
end
-
+
protected
def options_include_default?(options)
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
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/core_ext/module/attr_internal.rb b/activesupport/lib/active_support/core_ext/module/attr_internal.rb
index e0be31090c..b66c0d7500 100644
--- a/activesupport/lib/active_support/core_ext/module/attr_internal.rb
+++ b/activesupport/lib/active_support/core_ext/module/attr_internal.rb
@@ -1,19 +1,19 @@
class Module
- # Declare an attribute reader backed by an internally-named instance variable.
+ # Declares an attribute reader backed by an internally-named instance variable.
def attr_internal_reader(*attrs)
attrs.each do |attr|
module_eval "def #{attr}() #{attr_internal_ivar_name(attr)} end"
end
end
- # Declare an attribute writer backed by an internally-named instance variable.
+ # Declares an attribute writer backed by an internally-named instance variable.
def attr_internal_writer(*attrs)
attrs.each do |attr|
module_eval "def #{attr}=(v) #{attr_internal_ivar_name(attr)} = v end"
end
end
- # Declare an attribute reader and writer backed by an internally-named instance
+ # Declares an attribute reader and writer backed by an internally-named instance
# variable.
def attr_internal_accessor(*attrs)
attr_internal_reader(*attrs)
diff --git a/activesupport/lib/active_support/core_ext/range/conversions.rb b/activesupport/lib/active_support/core_ext/range/conversions.rb
index 8d757646d3..932bdedad3 100644
--- a/activesupport/lib/active_support/core_ext/range/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/range/conversions.rb
@@ -15,10 +15,9 @@ module ActiveSupport #:nodoc:
end
# Gives a human readable format of the range.
#
- # Example:
+ # ==== Example:
#
- # >> [1..100].to_formatted_s
- # => "1..100"
+ # [1..100].to_formatted_s # => "1..100"
def to_formatted_s(format = :default)
RANGE_FORMATS[format] ? RANGE_FORMATS[format].call(first, last) : to_default_s
end
diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb
index 68fbf3da35..0fd44324bb 100644
--- a/activesupport/lib/active_support/inflector.rb
+++ b/activesupport/lib/active_support/inflector.rb
@@ -92,6 +92,13 @@ module Inflector
extend self
+ # Yields a singleton instance of Inflector::Inflections so you can specify additional
+ # inflector rules.
+ #
+ # Example:
+ # Inflector.inflections do |inflect|
+ # inflect.uncountable "rails"
+ # end
def inflections
if block_given?
yield Inflections.instance
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/README b/railties/README
index b5f4eee4b7..2af0fb1133 100644
--- a/railties/README
+++ b/railties/README
@@ -145,7 +145,9 @@ and also on programming in general.
Debugger support is available through the debugger command when you start your Mongrel or
Webrick server with --debugger. This means that you can break out of execution at any point
-in the code, investigate and change the model, AND then resume execution! Example:
+in the code, investigate and change the model, AND then resume execution!
+You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'
+Example:
class WeblogController < ActionController::Base
def index
@@ -183,6 +185,13 @@ Passing an argument will specify a different environment, like <tt>script/consol
To reload your controllers and models after launching the console run <tt>reload!</tt>
+== dbconsole
+
+You can go to the command line of your database directly through <tt>script/dbconsole</tt>.
+You would be connected to the database with the credentials defined in database.yml.
+Starting the script without arguments will connect you to the development database. Passing an
+argument will connect you to a different database, like <tt>script/dbconsole production</tt>.
+Currently works for mysql, postgresql and sqlite.
== Description of Contents
@@ -200,13 +209,13 @@ app/models
app/views
Holds the template files for the view that should be named like
- weblogs/index.erb for the WeblogsController#index action. All views use eRuby
+ weblogs/index.html.erb for the WeblogsController#index action. All views use eRuby
syntax.
app/views/layouts
Holds the template files for layouts to be used with views. This models the common
header/footer method of wrapping views. In your views, define a layout using the
- <tt>layout :default</tt> and create a file named default.erb. Inside default.erb,
+ <tt>layout :default</tt> and create a file named default.html.erb. Inside default.html.erb,
call <% yield %> to render the view using this layout.
app/helpers
@@ -243,4 +252,5 @@ test
vendor
External libraries that the application depends on. Also includes the plugins subdirectory.
+ If the app has frozen rails, those gems also go here, under vendor/rails/.
This directory is in the load path.
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