aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorBrian Lopez <seniorlopez@gmail.com>2010-08-02 13:30:08 -0700
committerBrian Lopez <seniorlopez@gmail.com>2010-08-02 13:30:08 -0700
commit954de9940b8f212cafdb9f5e97abc83d27b3e4ff (patch)
tree2cc9e60c1967cc217d55dcb187c4bcfc24c44cd5 /activerecord
parent2353e826b0446917eb655b60c47533f5a9c2a07f (diff)
parent88b5f938cf7d3eb26ad204451a4dbb9c2cf4f571 (diff)
downloadrails-954de9940b8f212cafdb9f5e97abc83d27b3e4ff.tar.gz
rails-954de9940b8f212cafdb9f5e97abc83d27b3e4ff.tar.bz2
rails-954de9940b8f212cafdb9f5e97abc83d27b3e4ff.zip
Merge branch 'master' into mysql2
* master: Bring returning back to ease migration. Remove duplicated logic. Eager loading an association should not change the count of children fix loading of different elements in array then int and string [#5036 state:resolved] Tidy up previous commit. test and fix collection_singular_ids= with string primary keys [#5125 state:resolved] Handle edge cases in the previous patch. Improved how AppGenerator generates the application name. It now detects the current app name whenever possible. This means that renaming the residing directory will not effect the app name generated by AppGenerator. ActiveModel::Errors json serialization to work as Rails 3b4 [#5254 state:resolved] Add missing require in ActiveSupport::HashWithIndifferentAccess [#5189 state:resolved] Add an internal (private API) after_touch callback. [#5271 state:resolved] added failing touch propagation test Makes rails destroy scaffold don't duplicate routes.draw do |map| |map| when using the deprecated syntax Failing test to check for route file corruption if legacy map parameter is used. [#5263 state:open] Corrected the rake test:units and test:functionals description [#5251 state:committed] Use AS::OrderedHash when trusting in the order of the hash Fix label form helper to use I18n and html options, without the need of 'nil' text param:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/associations.rb19
-rw-r--r--activerecord/lib/active_record/callbacks.rb8
-rw-r--r--activerecord/lib/active_record/persistence.rb13
-rw-r--r--activerecord/lib/active_record/reflection.rb26
-rw-r--r--activerecord/lib/active_record/timestamp.rb13
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb40
-rw-r--r--activerecord/test/cases/associations_test.rb13
-rw-r--r--activerecord/test/cases/timestamp_test.rb19
-rw-r--r--activerecord/test/fixtures/subscriptions.yml2
-rw-r--r--activerecord/test/models/book.rb3
-rw-r--r--activerecord/test/models/electron.rb3
-rw-r--r--activerecord/test/models/liquid.rb5
-rw-r--r--activerecord/test/models/molecule.rb4
-rw-r--r--activerecord/test/schema/schema.rb15
14 files changed, 146 insertions, 37 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index f540aa7f25..fdc203e298 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1437,9 +1437,11 @@ module ActiveRecord
association.replace(new_value)
association
end
-
+
redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
- ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i)
+ pk_column = reflection.primary_key_column
+ ids = (new_value || []).reject { |nid| nid.blank? }
+ ids.map!{ |i| pk_column.type_cast(i) }
send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids))
end
end
@@ -1498,6 +1500,7 @@ module ActiveRecord
end
end
after_save(method_name)
+ after_touch(method_name)
after_destroy(method_name)
end
@@ -1802,9 +1805,7 @@ module ActiveRecord
case associations
when Symbol, String
reflection = base.reflections[associations]
- if reflection && reflection.collection?
- records.each { |record| record.send(reflection.name).target.uniq! }
- end
+ remove_uniq_by_reflection(reflection, records)
when Array
associations.each do |association|
remove_duplicate_results!(base, records, association)
@@ -1812,6 +1813,7 @@ module ActiveRecord
when Hash
associations.keys.each do |name|
reflection = base.reflections[name]
+ remove_uniq_by_reflection(reflection, records)
parent_records = []
records.each do |record|
@@ -1830,6 +1832,7 @@ module ActiveRecord
end
protected
+
def build(associations, parent = nil, join_class = Arel::InnerJoin)
parent ||= @joins.last
case associations
@@ -1852,6 +1855,12 @@ module ActiveRecord
end
end
+ def remove_uniq_by_reflection(reflection, records)
+ if reflection && reflection.collection?
+ records.each { |record| record.send(reflection.name).target.uniq! }
+ end
+ end
+
def build_join_association(reflection, parent)
JoinAssociation.new(reflection, self, parent)
end
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index 637dac450b..82c45a41b0 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -228,7 +228,7 @@ module ActiveRecord
extend ActiveSupport::Concern
CALLBACKS = [
- :after_initialize, :after_find, :before_validation, :after_validation,
+ :after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
:before_save, :around_save, :after_save, :before_create, :around_create,
:after_create, :before_update, :around_update, :after_update,
:before_destroy, :around_destroy, :after_destroy
@@ -238,7 +238,7 @@ module ActiveRecord
extend ActiveModel::Callbacks
include ActiveModel::Validations::Callbacks
- define_model_callbacks :initialize, :find, :only => :after
+ define_model_callbacks :initialize, :find, :touch, :only => :after
define_model_callbacks :save, :create, :update, :destroy
end
@@ -256,6 +256,10 @@ module ActiveRecord
_run_destroy_callbacks { super }
end
+ def touch(*) #:nodoc:
+ _run_touch_callbacks { super }
+ end
+
def deprecated_callback_method(symbol) #:nodoc:
if respond_to?(symbol, true)
ActiveSupport::Deprecation.warn("Overwriting #{symbol} in your models has been deprecated, please use Base##{symbol} :method_name instead")
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 38b91652ee..cbc2220e96 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -218,6 +218,19 @@ module ActiveRecord
self
end
+ # Saves the record with the updated_at/on attributes set to the current time.
+ # Please note that no validation is performed and no callbacks are executed.
+ # If an attribute name is passed, that attribute is updated along with
+ # updated_at/on attributes.
+ #
+ # Examples:
+ #
+ # product.touch # updates updated_at/on
+ # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
+ def touch(attribute = nil)
+ update_attribute(attribute, current_time_from_proper_timezone)
+ end
+
private
def create_or_update
raise ReadOnlyRecord if readonly?
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 03a932f642..7f47a812eb 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -91,25 +91,19 @@ module ActiveRecord
#
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:balance</tt>
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
- def name
- @name
- end
+ attr_reader :name
# Returns the macro type.
#
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:composed_of</tt>
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
- def macro
- @macro
- end
+ attr_reader :macro
# Returns the hash of options used for the macro.
#
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>{ :class_name => "Money" }</tt>
# <tt>has_many :clients</tt> returns +{}+
- def options
- @options
- end
+ attr_reader :options
# Returns the class for the macro.
#
@@ -137,11 +131,6 @@ module ActiveRecord
@sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
end
- # Returns +true+ if +self+ is a +belongs_to+ reflection.
- def belongs_to?
- macro == :belongs_to
- end
-
private
def derive_class_name
name.to_s.camelize
@@ -213,6 +202,10 @@ module ActiveRecord
@primary_key_name ||= options[:foreign_key] || derive_primary_key_name
end
+ def primary_key_column
+ @primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key }
+ end
+
def association_foreign_key
@association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key
end
@@ -307,6 +300,11 @@ module ActiveRecord
dependent_conditions
end
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
+ def belongs_to?
+ macro == :belongs_to
+ end
+
private
def derive_class_name
class_name = name.to_s.camelize
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 92f7a7753d..32b3f03f13 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -31,19 +31,6 @@ module ActiveRecord
class_inheritable_accessor :record_timestamps, :instance_writer => false
self.record_timestamps = true
end
-
- # Saves the record with the updated_at/on attributes set to the current time.
- # Please note that no validation is performed and no callbacks are executed.
- # If an attribute name is passed, that attribute is updated along with
- # updated_at/on attributes.
- #
- # Examples:
- #
- # product.touch # updates updated_at/on
- # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
- def touch(attribute = nil)
- update_attribute(attribute, current_time_from_proper_timezone)
- end
private
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index e4dd810732..0eaadac5ae 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -14,9 +14,14 @@ require 'models/toy'
require 'models/contract'
require 'models/company'
require 'models/developer'
+require 'models/subscriber'
+require 'models/book'
+require 'models/subscription'
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
- fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys, :jobs, :references, :companies
+ fixtures :posts, :readers, :people, :comments, :authors,
+ :owners, :pets, :toys, :jobs, :references, :companies,
+ :subscribers, :books, :subscriptions, :developers
# Dummies to force column loads so query counts are clean.
def setup
@@ -383,4 +388,37 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
end
+
+ def test_collection_singular_ids_getter_with_string_primary_keys
+ book = books(:awdr)
+ assert_equal 2, book.subscriber_ids.size
+ assert_equal [subscribers(:first).nick, subscribers(:second).nick].sort, book.subscriber_ids.sort
+ end
+
+ def test_collection_singular_ids_setter
+ company = companies(:rails_core)
+ dev = Developer.find(:first)
+
+ company.developer_ids = [dev.id]
+ assert_equal [dev], company.developers
+ end
+
+ def test_collection_singular_ids_setter_with_string_primary_keys
+ assert_nothing_raised do
+ book = books(:awdr)
+ book.subscriber_ids = [subscribers(:second).nick]
+ assert_equal [subscribers(:second)], book.subscribers(true)
+
+ book.subscriber_ids = []
+ assert_equal [], book.subscribers(true)
+ end
+
+ end
+
+ def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
+ company = companies(:rails_core)
+ ids = [Developer.find(:first).id, -9999]
+ assert_raises(ActiveRecord::RecordNotFound) {company.developer_ids= ids}
+ end
+
end
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index a1c794c084..d328ca630b 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -14,11 +14,24 @@ require 'models/reader'
require 'models/parrot'
require 'models/ship_part'
require 'models/ship'
+require 'models/liquid'
+require 'models/molecule'
+require 'models/electron'
class AssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
:computers, :people, :readers
+ def test_eager_loading_should_not_change_count_of_children
+ liquid = Liquid.create(:name => 'salty')
+ molecule = liquid.molecules.create(:name => 'molecule_1')
+ molecule.electrons.create(:name => 'electron_1')
+ molecule.electrons.create(:name => 'electron_2')
+
+ liquids = Liquid.includes(:molecules => :electrons).where('molecules.id is not null')
+ assert_equal 1, liquids[0].molecules.length
+ end
+
def test_loading_the_association_target_should_keep_child_records_marked_for_destruction
ship = Ship.create!(:name => "The good ship Dollypop")
part = ship.parts.create!(:name => "Mast")
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index f765540808..06ab7aa9c7 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -2,9 +2,10 @@ require 'cases/helper'
require 'models/developer'
require 'models/owner'
require 'models/pet'
+require 'models/toy'
class TimestampTest < ActiveRecord::TestCase
- fixtures :developers, :owners, :pets
+ fixtures :developers, :owners, :pets, :toys
def setup
@developer = Developer.first
@@ -82,4 +83,20 @@ class TimestampTest < ActiveRecord::TestCase
ensure
Pet.belongs_to :owner, :touch => true
end
+
+ def test_touching_a_record_touches_parent_record_and_grandparent_record
+ Toy.belongs_to :pet, :touch => true
+ Pet.belongs_to :owner, :touch => true
+
+ toy = Toy.first
+ pet = toy.pet
+ owner = pet.owner
+
+ owner.update_attribute(:updated_at, (time = 3.days.ago))
+ toy.touch
+
+ assert_not_equal time, owner.updated_at
+ ensure
+ Toy.belongs_to :pet
+ end
end
diff --git a/activerecord/test/fixtures/subscriptions.yml b/activerecord/test/fixtures/subscriptions.yml
index 371bfd3422..5a93c12193 100644
--- a/activerecord/test/fixtures/subscriptions.yml
+++ b/activerecord/test/fixtures/subscriptions.yml
@@ -9,4 +9,4 @@ webster_rfr:
alterself_awdr:
id: 3
subscriber_id: alterself
- book_id: 3 \ No newline at end of file
+ book_id: 1
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index cfd07abddc..1e030b4f59 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -1,4 +1,7 @@
class Book < ActiveRecord::Base
has_many :citations, :foreign_key => 'book1_id'
has_many :references, :through => :citations, :source => :reference_of, :uniq => true
+
+ has_many :subscriptions
+ has_many :subscribers, :through => :subscriptions
end
diff --git a/activerecord/test/models/electron.rb b/activerecord/test/models/electron.rb
new file mode 100644
index 0000000000..35af9f679b
--- /dev/null
+++ b/activerecord/test/models/electron.rb
@@ -0,0 +1,3 @@
+class Electron < ActiveRecord::Base
+ belongs_to :molecule
+end
diff --git a/activerecord/test/models/liquid.rb b/activerecord/test/models/liquid.rb
new file mode 100644
index 0000000000..b96c054f6c
--- /dev/null
+++ b/activerecord/test/models/liquid.rb
@@ -0,0 +1,5 @@
+class Liquid < ActiveRecord::Base
+ set_table_name :liquid
+ has_many :molecules, :uniq => true
+end
+
diff --git a/activerecord/test/models/molecule.rb b/activerecord/test/models/molecule.rb
new file mode 100644
index 0000000000..69325b8d29
--- /dev/null
+++ b/activerecord/test/models/molecule.rb
@@ -0,0 +1,4 @@
+class Molecule < ActiveRecord::Base
+ belongs_to :liquid
+ has_many :electrons
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index f3fd37cd61..fc3810f82b 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -398,6 +398,7 @@ ActiveRecord::Schema.define do
create_table :pets, :primary_key => :pet_id ,:force => true do |t|
t.string :name
t.integer :owner_id, :integer
+ t.timestamps
end
create_table :pirates, :force => true do |t|
@@ -530,6 +531,7 @@ ActiveRecord::Schema.define do
create_table :toys, :primary_key => :toy_id ,:force => true do |t|
t.string :name
t.integer :pet_id, :integer
+ t.timestamps
end
create_table :traffic_lights, :force => true do |t|
@@ -616,6 +618,19 @@ ActiveRecord::Schema.define do
t.datetime :updated_at
end
+ create_table :liquid, :force => true do |t|
+ t.string :name
+ end
+ create_table :molecules, :force => true do |t|
+ t.integer :liquid_id
+ t.string :name
+ end
+ create_table :electrons, :force => true do |t|
+ t.integer :molecule_id
+ t.string :name
+ 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|