aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb2
-rwxr-xr-xactiverecord/lib/active_record/base.rb17
-rw-r--r--activerecord/lib/active_record/calculations.rb2
-rw-r--r--activerecord/lib/active_record/dirty.rb4
-rw-r--r--activerecord/lib/active_record/migration.rb32
-rw-r--r--activerecord/lib/active_record/named_scope.rb6
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb2
-rw-r--r--activerecord/test/cases/associations/eager_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb13
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb2
-rw-r--r--activerecord/test/cases/base_test.rb8
-rw-r--r--activerecord/test/cases/dirty_test.rb12
-rw-r--r--activerecord/test/cases/lifecycle_test.rb2
-rw-r--r--activerecord/test/cases/method_scoping_test.rb2
-rw-r--r--activerecord/test/cases/migration_test.rb20
-rw-r--r--activerecord/test/cases/named_scope_test.rb13
-rw-r--r--activerecord/test/cases/query_cache_test.rb2
17 files changed, 117 insertions, 24 deletions
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index a28be9eed1..9061037b39 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -344,7 +344,7 @@ module ActiveRecord
callback(:before_add, record)
yield(record) if block_given?
@target ||= [] unless loaded?
- @target << record
+ @target << record unless @reflection.options[:uniq] && @target.include?(record)
callback(:after_add, record)
record
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index cf7ae97452..8e40b331d9 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -2599,7 +2599,7 @@ module ActiveRecord #:nodoc:
removed_attributes = attributes.keys - safe_attributes.keys
if removed_attributes.any?
- logger.debug "WARNING: Can't mass-assign these protected attributes: #{removed_attributes.join(', ')}"
+ log_protected_attribute_removal(removed_attributes)
end
safe_attributes
@@ -2614,6 +2614,10 @@ module ActiveRecord #:nodoc:
end
end
+ def log_protected_attribute_removal(*attributes)
+ logger.debug "WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}"
+ end
+
# The primary key and inheritance column can never be set by mass-assignment for security reasons.
def attributes_protected_by_default
default = [ self.class.primary_key, self.class.inheritance_column ]
@@ -2627,8 +2631,15 @@ module ActiveRecord #:nodoc:
quoted = {}
connection = self.class.connection
attribute_names.each do |name|
- if column = column_for_attribute(name)
- quoted[name] = connection.quote(read_attribute(name), column) unless !include_primary_key && column.primary
+ if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
+ value = read_attribute(name)
+
+ # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
+ if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))
+ value = value.to_yaml
+ end
+
+ quoted[name] = connection.quote(value, column)
end
end
include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb
index 34ffc9a5e5..e765b46cc2 100644
--- a/activerecord/lib/active_record/calculations.rb
+++ b/activerecord/lib/active_record/calculations.rb
@@ -211,7 +211,7 @@ module ActiveRecord
sql << " ORDER BY #{options[:order]} " if options[:order]
add_limit!(sql, options, scope)
- sql << ')' if use_workaround
+ sql << ') AS #{aggregate_alias}_subquery' if use_workaround
sql
end
diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb
index 4ce0356457..63bf8c8f5b 100644
--- a/activerecord/lib/active_record/dirty.rb
+++ b/activerecord/lib/active_record/dirty.rb
@@ -134,7 +134,9 @@ module ActiveRecord
def update_with_dirty
if partial_updates?
- update_without_dirty(changed)
+ # Serialized attributes should always be written in case they've been
+ # changed in place.
+ update_without_dirty(changed | self.class.serialized_attributes.keys)
else
update_without_dirty
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 731a350854..fd77f27b77 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -349,6 +349,27 @@ module ActiveRecord
end
end
+ # MigrationProxy is used to defer loading of the actual migration classes
+ # until they are needed
+ class MigrationProxy
+
+ attr_accessor :name, :version, :filename
+
+ delegate :migrate, :announce, :write, :to=>:migration
+
+ private
+
+ def migration
+ @migration ||= load_migration
+ end
+
+ def load_migration
+ load(filename)
+ name.constantize
+ end
+
+ end
+
class Migrator#:nodoc:
class << self
def migrate(migrations_path, target_version = nil)
@@ -437,7 +458,7 @@ module ActiveRecord
runnable.pop if down? && !target.nil?
runnable.each do |migration|
- Base.logger.info "Migrating to #{migration} (#{migration.version})"
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})"
# On our way up, we skip migrating the ones we've already migrated
# On our way down, we skip reverting the ones we've never migrated
@@ -470,11 +491,10 @@ module ActiveRecord
raise DuplicateMigrationNameError.new(name.camelize)
end
- load(file)
-
- klasses << returning(name.camelize.constantize) do |klass|
- class << klass; attr_accessor :version end
- klass.version = version
+ klasses << returning(MigrationProxy.new) do |migration|
+ migration.name = name.camelize
+ migration.version = version
+ migration.filename = file
end
end
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 7f274543b6..eb887ee550 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -103,7 +103,7 @@ module ActiveRecord
attr_reader :proxy_scope, :proxy_options
[].methods.each do |m|
- unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate|first|last|empty?|any?)/
+ unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate|first|last|empty?|any?|respond_to?)/
delegate m, :to => :proxy_found
end
end
@@ -140,6 +140,10 @@ module ActiveRecord
@found ? @found.empty? : count.zero?
end
+ def respond_to?(method)
+ super || @proxy_scope.respond_to?(method)
+ end
+
def any?
if block_given?
proxy_found.any? { |*block_args| yield(*block_args) }
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 3631be76a0..1f8a1090eb 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -9,7 +9,7 @@ require 'models/topic'
require 'models/reply'
class CascadedEagerLoadingTest < ActiveRecord::TestCase
- fixtures :authors, :mixins, :companies, :posts, :topics
+ fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments, :categorizations
def test_eager_association_loading_with_cascaded_two_levels
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index f65ada550b..58506574f8 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -21,7 +21,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :categories, :categories_posts,
:companies, :accounts, :tags, :taggings, :people, :readers,
:owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books,
- :developers, :projects
+ :developers, :projects, :developers_projects
def test_loading_with_one_association
posts = Post.find(:all, :include => :comments)
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index b29df68d22..f71b122ff0 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -70,7 +70,7 @@ end
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
- :parrots, :pirates, :treasures, :price_estimates
+ :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
def test_has_and_belongs_to_many
david = Developer.find(1)
@@ -299,6 +299,17 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 3, projects(:active_record, :reload).developers.size
end
+ def test_uniq_option_prevents_duplicate_push
+ project = projects(:active_record)
+ project.developers << developers(:jamis)
+ project.developers << developers(:david)
+ assert_equal 3, project.developers.size
+
+ project.developers << developers(:david)
+ project.developers << developers(:jamis)
+ assert_equal 3, project.developers.size
+ end
+
def test_deleting
david = Developer.find(1)
active_record = Project.find(1)
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 47e4b3527d..b806e885e1 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -14,7 +14,7 @@ require 'models/reader'
class HasManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :categories, :companies, :developers, :projects,
:developers_projects, :topics, :authors, :comments, :author_addresses,
- :people, :posts
+ :people, :posts, :readers
def setup
Client.destroyed_client_ids.clear
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index e6d1b5ddfd..36d30ade5e 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -76,7 +76,7 @@ class TopicWithProtectedContentAndAccessibleAuthorName < ActiveRecord::Base
end
class BasicsTest < ActiveRecord::TestCase
- fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories
def test_table_exists
assert !NonExistentTable.table_exists?
@@ -1361,6 +1361,12 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal(myobj, topic.content)
end
+ def test_serialized_time_attribute
+ myobj = Time.local(2008,1,1,1,0)
+ topic = Topic.create("content" => myobj).reload
+ assert_equal(myobj, topic.content)
+ end
+
def test_nil_serialized_attribute_with_class_constraint
myobj = MyObject.new('value1', 'value2')
topic = Topic.new
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index e5e022050d..feb47a15a8 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -191,6 +191,18 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.changed?
end
+ def test_save_should_store_serialized_attributes_even_with_partial_updates
+ with_partial_updates(Topic) do
+ topic = Topic.create!(:content => {:a => "a"})
+ topic.content[:b] = "b"
+ #assert topic.changed? # Known bug, will fail
+ topic.save!
+ assert_equal "b", topic.content[:b]
+ topic.reload
+ assert_equal "b", topic.content[:b]
+ end
+ end
+
private
def with_partial_updates(klass, on = true)
old = klass.partial_updates?
diff --git a/activerecord/test/cases/lifecycle_test.rb b/activerecord/test/cases/lifecycle_test.rb
index ab005c6b00..54fb3d8c39 100644
--- a/activerecord/test/cases/lifecycle_test.rb
+++ b/activerecord/test/cases/lifecycle_test.rb
@@ -74,7 +74,7 @@ class MultiObserver < ActiveRecord::Observer
end
class LifecycleTest < ActiveRecord::TestCase
- fixtures :topics, :developers
+ fixtures :topics, :developers, :minimalistics
def test_before_destroy
original_count = Topic.count
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index d6b3e341df..ee66ac948d 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -6,7 +6,7 @@ require 'models/post'
require 'models/category'
class MethodScopingTest < ActiveRecord::TestCase
- fixtures :developers, :projects, :comments, :posts
+ fixtures :developers, :projects, :comments, :posts, :developers_projects
def test_set_conditions
Developer.with_scope(:find => { :conditions => 'just a test...' }) do
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 7ecf755ef8..920f719995 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -922,6 +922,26 @@ if ActiveRecord::Base.connection.supports_migrations?
migrations[0].name == 'innocent_jointable'
end
+ def test_only_loads_pending_migrations
+ # migrate up to 1
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
+
+ # now unload the migrations that have been defined
+ PeopleHaveLastNames.unloadable
+ ActiveSupport::Dependencies.remove_unloadable_constants!
+
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
+
+ assert !defined? PeopleHaveLastNames
+
+ %w(WeNeedReminders, InnocentJointable).each do |migration|
+ assert defined? migration
+ end
+
+ ensure
+ load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
+ end
+
def test_migrator_interleaved_migrations
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index e21ffbbdba..bd6ec23853 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -45,6 +45,12 @@ class NamedScopeTest < ActiveRecord::TestCase
assert_equal Topic.average(:replies_count), Topic.base.average(:replies_count)
end
+ def test_scope_should_respond_to_own_methods_and_methods_of_the_proxy
+ assert Topic.approved.respond_to?(:proxy_found)
+ assert Topic.approved.respond_to?(:count)
+ assert Topic.approved.respond_to?(:length)
+ end
+
def test_subclasses_inherit_scopes
assert Topic.scopes.include?(:base)
@@ -186,9 +192,10 @@ class NamedScopeTest < ActiveRecord::TestCase
def test_any_should_not_load_results
topics = Topic.base
- assert_queries(1) do
- topics.expects(:empty?).returns(true)
- assert !topics.any?
+ assert_queries(2) do
+ topics.any? # use count query
+ topics.collect # force load
+ topics.any? # use loaded (no query)
end
end
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index dc9eeec281..eae2104531 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -58,7 +58,7 @@ end
uses_mocha 'QueryCacheExpiryTest' do
class QueryCacheExpiryTest < ActiveRecord::TestCase
- fixtures :tasks
+ fixtures :tasks, :posts, :categories, :categories_posts
def test_find
Task.connection.expects(:clear_query_cache).times(1)