diff options
Diffstat (limited to 'activerecord')
15 files changed, 239 insertions, 162 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 0955761b7f..ab1eb49286 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,34 @@ +* Ambiguous reflections are on :through relationships are no longer supported. + For example, you need to change this: + + class Author < ActiveRecord::Base + has_many :posts + has_many :taggings, :through => :posts + end + + class Post < ActiveRecord::Base + has_one :tagging + has_many :taggings + end + + class Tagging < ActiveRecord::Base + end + + To this: + + class Author < ActiveRecord::Base + has_many :posts + has_many :taggings, :through => :posts, :source => :tagging + end + + class Post < ActiveRecord::Base + has_one :tagging + has_many :taggings + end + + class Tagging < ActiveRecord::Base + end + * Remove column restrictions for `count`, let the database raise if the SQL is invalid. The previous behavior was untested and surprising for the user. Fixes #5554. @@ -139,8 +170,8 @@ *Olek Janiszewski* -* fixes bug introduced by #3329. Now, when autosaving associations, - deletions happen before inserts and saves. This prevents a 'duplicate +* fixes bug introduced by #3329. Now, when autosaving associations, + deletions happen before inserts and saves. This prevents a 'duplicate unique value' database error that would occur if a record being created had the same value on a unique indexed field as that of a record being destroyed. diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index 608a6af16c..ee62298793 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -200,13 +200,14 @@ module ActiveRecord creation_attributes.each { |key, value| record[key] = value } end - # Should be true if there is a foreign key present on the owner which + # Returns true if there is a foreign key present on the owner which # references the target. This is used to determine whether we can load # the target if the owner is currently a new record (and therefore - # without a key). + # without a key). If the owner is a new record then foreign_key must + # be present in order to load target. # # Currently implemented by belongs_to (vanilla and polymorphic) and - # has_one/has_many :through associations which go through a belongs_to + # has_one/has_many :through associations which go through a belongs_to. def foreign_key_present? false end 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 c982d65d65..0be4b5cb19 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -25,6 +25,9 @@ module ActiveRecord end end + class ChangeColumnDefinition < Struct.new(:column, :type, :options) #:nodoc: + end + # Represents the schema of an SQL table in an abstract way. This class # provides methods for manipulating the schema representation. # 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 2a4eda3622..3ac55a0f11 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -694,17 +694,6 @@ module ActiveRecord end end - def add_column_options!(sql, options) #:nodoc: - sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options) - # must explicitly check for :null to allow change_column to work on migrations - if options[:null] == false - sql << " NOT NULL" - end - if options[:auto_increment] == true - sql << " AUTO_INCREMENT" - end - end - # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause. # # distinct("posts.id", ["posts.created_at desc"]) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 26586f0974..e232cad982 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -116,6 +116,12 @@ module ActiveRecord send m, o end + def visit_AddColumn(o) + sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale) + sql = "ADD #{quote_column_name(o.name)} #{sql_type}" + add_column_options!(sql, column_options(o)) + end + private def visit_AlterTable(o) @@ -123,12 +129,6 @@ module ActiveRecord sql << o.adds.map { |col| visit_AddColumn col }.join(' ') end - def visit_AddColumn(o) - sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale) - sql = "ADD #{quote_column_name(o.name)} #{sql_type}" - add_column_options!(sql, column_options(o)) - end - def visit_ColumnDefinition(o) sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale) column_sql = "#{quote_column_name(o.name)} #{sql_type}" @@ -149,6 +149,8 @@ module ActiveRecord column_options[:null] = o.null unless o.null.nil? column_options[:default] = o.default unless o.default.nil? column_options[:column] = o + column_options[:first] = o.first + column_options[:after] = o.after column_options end @@ -164,9 +166,20 @@ module ActiveRecord @conn.type_to_sql type.to_sym, limit, precision, scale end - def add_column_options!(column_sql, column_options) - @conn.add_column_options! column_sql, column_options - column_sql + def add_column_options!(sql, options) + sql << " DEFAULT #{@conn.quote(options[:default], options[:column])}" if options_include_default?(options) + # must explicitly check for :null to allow change_column to work on migrations + if options[:null] == false + sql << " NOT NULL" + end + if options[:auto_increment] == true + sql << " AUTO_INCREMENT" + end + sql + end + + def options_include_default?(options) + options.include?(:default) && !(options[:null] == false && options[:default].nil?) end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index d098ded77c..5b25b26164 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -4,17 +4,26 @@ module ActiveRecord module ConnectionAdapters class AbstractMysqlAdapter < AbstractAdapter class SchemaCreation < AbstractAdapter::SchemaCreation - private def visit_AddColumn(o) - add_column_position!(super, o) + add_column_position!(super, column_options(o)) + end + + private + def visit_ChangeColumnDefinition(o) + column = o.column + options = o.options + sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale]) + change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}" + add_column_options!(change_column_sql, options) + add_column_position!(change_column_sql, options) end - def add_column_position!(sql, column) - if column.first + def add_column_position!(sql, options) + if options[:first] sql << " FIRST" - elsif column.after - sql << " AFTER #{quote_column_name(column.after)}" + elsif options[:after] + sql << " AFTER #{quote_column_name(options[:after])}" end sql end @@ -661,10 +670,9 @@ module ActiveRecord end def add_column_sql(table_name, column_name, type, options = {}) - add_column_sql = "ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - add_column_options!(add_column_sql, options) - add_column_position!(add_column_sql, options) - add_column_sql + td = create_table_definition table_name, options[:temporary], options[:options] + cd = td.new_column_definition(column_name, type, options) + schema_creation.visit_AddColumn cd end def change_column_sql(table_name, column_name, type, options = {}) @@ -678,14 +686,12 @@ module ActiveRecord options[:null] = column.null end - change_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - add_column_options!(change_column_sql, options) - add_column_position!(change_column_sql, options) - change_column_sql + options[:name] = column.name + schema_creation.accept ChangeColumnDefinition.new column, type, options end def rename_column_sql(table_name, column_name, new_column_name) - options = {} + options = { name: new_column_name } if column = columns(table_name).find { |c| c.name == column_name.to_s } options[:default] = column.default @@ -696,9 +702,7 @@ module ActiveRecord end current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"] - rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}" - add_column_options!(rename_column_sql, options) - rename_column_sql + schema_creation.accept ChangeColumnDefinition.new column, current_type, options end def remove_column_sql(table_name, column_name, type = nil, options = {}) diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index 81cca37939..e1faadf1ab 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -50,7 +50,7 @@ module ActiveRecord # ==== Parameters # # * +id+ - The id of the object you wish to update a counter on or an Array of ids. - # * +counters+ - An Array of Hashes containing the names of the fields + # * +counters+ - A Hash containing the names of the fields # to update as keys and the amount to update the field by as values. # # ==== Examples diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index 40976bc29e..e826762def 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -5,7 +5,7 @@ module ActiveRecord extend ActiveSupport::Concern included do - # Determine whether to store the full constant name including namespace when using STI + # Determines whether to store the full constant name including namespace when using STI. class_attribute :store_full_sti_class, instance_writer: false self.store_full_sti_class = true end @@ -13,7 +13,7 @@ module ActiveRecord module ClassMethods # Determines if one of the attributes passed in is the inheritance column, # and if the inheritance column is attr accessible, it initializes an - # instance of the given subclass instead of the base class + # instance of the given subclass instead of the base class. def new(*args, &block) if abstract_class? || self == Base raise NotImplementedError, "#{self} is an abstract class and can not be instantiated." @@ -27,7 +27,8 @@ module ActiveRecord super end - # True if this isn't a concrete subclass needing a STI type condition. + # Returns +true+ if this does not need STI type condition. Returns + # +false+ if STI type condition needs to be applied. def descends_from_active_record? if self == Base false diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 0e8822d63f..e53e8553ad 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -306,14 +306,9 @@ module ActiveRecord attr_names.each do |association_name| if reflection = reflect_on_association(association_name) - reflection.options[:autosave] = true + reflection.autosave = true add_autosave_association_callbacks(reflection) - # Clear cached values of any inverse associations found in the - # reflection and prevent the reflection from finding inverses - # automatically in the future. - reflection.remove_automatic_inverse_of! - nested_attributes_options = self.nested_attributes_options.dup nested_attributes_options[association_name.to_sym] = options self.nested_attributes_options = nested_attributes_options diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 2aec73bd5b..8a9488656b 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -123,6 +123,11 @@ module ActiveRecord name.to_s.pluralize : name.to_s end + def autosave=(autosave) + @automatic_inverse_of = false + @options[:autosave] = autosave + end + # Returns the class for the macro. # # <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class @@ -184,10 +189,14 @@ module ActiveRecord @klass ||= active_record.send(:compute_type, class_name) end + attr_reader :type, :foreign_type + def initialize(*args) super @collection = [:has_many, :has_and_belongs_to_many].include?(macro) @automatic_inverse_of = nil + @type = options[:as] && "#{options[:as]}_type" + @foreign_type = options[:foreign_type] || "#{name}_type" end # Returns a new, unsaved instance of the associated class. +attributes+ will @@ -197,11 +206,11 @@ module ActiveRecord end def table_name - @table_name ||= klass.table_name + klass.table_name end def quoted_table_name - @quoted_table_name ||= klass.quoted_table_name + klass.quoted_table_name end def join_table @@ -212,16 +221,8 @@ module ActiveRecord @foreign_key ||= options[:foreign_key] || derive_foreign_key end - def foreign_type - @foreign_type ||= options[:foreign_type] || "#{name}_type" - end - - def type - @type ||= options[:as] && "#{options[:as]}_type" - end - def primary_key_column - @primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key } + klass.columns_hash[klass.primary_key] end def association_foreign_key @@ -245,14 +246,6 @@ module ActiveRecord end end - def columns(tbl_name) - @columns ||= klass.connection.columns(tbl_name) - end - - def reset_column_information - @columns = nil - end - def check_validity! check_validity_of_inverse! @@ -296,29 +289,13 @@ module ActiveRecord alias :source_macro :macro def has_inverse? - @options[:inverse_of] || find_inverse_of_automatically + inverse_name end def inverse_of - @inverse_of ||= if options[:inverse_of] - klass.reflect_on_association(options[:inverse_of]) - else - find_inverse_of_automatically - end - end + return unless inverse_name - # Clears the cached value of +@inverse_of+ on this object. This will - # not remove the :inverse_of option however, so future calls on the - # +inverse_of+ will have to recompute the inverse. - def clear_inverse_of_cache! - @inverse_of = nil - end - - # Removes the cached inverse association that was found automatically - # and prevents this object from finding the inverse association - # automatically in the future. - def remove_automatic_inverse_of! - @automatic_inverse_of = false + @inverse_of ||= klass.reflect_on_association inverse_name end def polymorphic_inverse_of(associated_class) @@ -393,26 +370,21 @@ module ActiveRecord INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key] private - # Attempts to find the inverse association automatically. - # If it cannot find a suitable inverse association, it returns + # Attempts to find the inverse association name automatically. + # If it cannot find a suitable inverse association name, it returns # nil. - def find_inverse_of_automatically - if @automatic_inverse_of == false - nil - elsif @automatic_inverse_of.nil? - set_automatic_inverse_of - else - klass.reflect_on_association(@automatic_inverse_of) + def inverse_name + options.fetch(:inverse_of) do + if @automatic_inverse_of == false + nil + else + @automatic_inverse_of ||= automatic_inverse_of + end end end - # Sets the +@automatic_inverse_of+ instance variable, and returns - # either nil or the inverse association that it finds. - # - # This method caches the inverse association that is found so that - # future calls to +find_inverse_of_automatically+ have much less - # overhead. - def set_automatic_inverse_of + # returns either nil or the inverse association name that it finds. + def automatic_inverse_of if can_find_inverse_of_automatically?(self) inverse_name = active_record.name.downcase.to_sym @@ -425,16 +397,11 @@ module ActiveRecord end if valid_inverse_reflection?(reflection) - @automatic_inverse_of = inverse_name - reflection - else - @automatic_inverse_of = false - nil + return inverse_name end - else - @automatic_inverse_of = false - nil end + + false end # Checks if the inverse reflection that is returned from the @@ -446,7 +413,7 @@ module ActiveRecord # from calling +klass+, +reflection+ will already be set to false. def valid_inverse_reflection?(reflection) reflection && - klass.name == reflection.active_record.try(:name) && + klass.name == reflection.active_record.name && klass.primary_key == reflection.active_record_primary_key && can_find_inverse_of_automatically?(reflection) end @@ -499,6 +466,11 @@ module ActiveRecord delegate :foreign_key, :foreign_type, :association_foreign_key, :active_record_primary_key, :type, :to => :source_reflection + def initialize(macro, name, scope, options, active_record) + super + @source_reflection_name = options[:source] + end + # Returns the source of the through reflection. It checks both a singularized # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>. # @@ -517,7 +489,7 @@ module ActiveRecord # # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags"> # def source_reflection - @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first + through_reflection.klass.reflect_on_association(source_reflection_name) end # Returns the AssociationReflection object specified in the <tt>:through</tt> option @@ -635,7 +607,32 @@ module ActiveRecord # # => [:tag, :tags] # def source_reflection_names - @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }.uniq + (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }.uniq + end + + def source_reflection_name # :nodoc: + return @source_reflection_name if @source_reflection_name + + names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq + names = names.find_all { |n| + through_reflection.klass.reflect_on_association(n) + } + + if names.length > 1 + example_options = options.dup + example_options[:source] = source_reflection_names.first + ActiveSupport::Deprecation.warn <<-eowarn +Ambiguous source reflection for through association. Please specify a :source +directive on your declaration like: + + class #{active_record.name} < ActiveRecord::Base + #{macro} :#{name}, #{example_options} + end + + eowarn + end + + @source_reflection_name = names.first end def source_options diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index b921f2eddb..41291844fc 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -11,7 +11,7 @@ module ActiveRecord # The #find_each method uses #find_in_batches with a batch size of 1000 (or as # specified by the +:batch_size+ option). # - # Person.all.find_each do |person| + # Person.find_each do |person| # person.do_awesome_stuff # end # @@ -19,8 +19,26 @@ module ActiveRecord # person.party_all_night! # end # - # You can also pass the +:start+ option to specify - # an offset to control the starting point. + # ==== Options + # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000. + # * <tt>:start</tt> - Specifies the starting point for the batch processing. + # This is especially useful if you want multiple workers dealing with + # the same processing queue. You can make worker 1 handle all the records + # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond + # (by setting the +:start+ option on that worker). + # + # # Let's process for a batch of 2000 records, skiping the first 2000 rows + # Person.find_each(start: 2000, batch_size: 2000) do |person| + # person.party_all_night! + # end + # + # NOTE: It's not possible to set the order. That is automatically set to + # ascending on the primary key ("id ASC") to make the batch ordering + # work. This also means that this method only works with integer-based + # primary keys. + # + # NOTE: You can't set the limit either, that's used to control + # the batch sizes. def find_each(options = {}) find_in_batches(options) do |records| records.each { |record| yield record } @@ -28,31 +46,33 @@ module ActiveRecord end # Yields each batch of records that was found by the find +options+ as - # an array. The size of each batch is set by the +:batch_size+ - # option; the default is 1000. - # - # You can control the starting point for the batch processing by - # supplying the +:start+ option. This is especially useful if you - # want multiple workers dealing with the same processing queue. You can - # make worker 1 handle all the records between id 0 and 10,000 and - # worker 2 handle from 10,000 and beyond (by setting the +:start+ - # option on that worker). - # - # It's not possible to set the order. That is automatically set to - # ascending on the primary key ("id ASC") to make the batch ordering - # work. This also means that this method only works with integer-based - # primary keys. You can't set the limit either, that's used to control - # the batch sizes. + # an array. # # Person.where("age > 21").find_in_batches do |group| # sleep(50) # Make sure it doesn't get too crowded in there! # group.each { |person| person.party_all_night! } # end # + # ==== Options + # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000. + # * <tt>:start</tt> - Specifies the starting point for the batch processing. + # This is especially useful if you want multiple workers dealing with + # the same processing queue. You can make worker 1 handle all the records + # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond + # (by setting the +:start+ option on that worker). + # # # Let's process the next 2000 records - # Person.all.find_in_batches(start: 2000, batch_size: 2000) do |group| + # Person.find_in_batches(start: 2000, batch_size: 2000) do |group| # group.each { |person| person.party_all_night! } # end + # + # NOTE: It's not possible to set the order. That is automatically set to + # ascending on the primary key ("id ASC") to make the batch ordering + # work. This also means that this method only works with integer-based + # primary keys. + # + # NOTE: You can't set the limit either, that's used to control + # the batch sizes. def find_in_batches(options = {}) options.assert_valid_keys(:start, :batch_size) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index cbb2803593..1550d702a6 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -11,9 +11,11 @@ module ActiveRecord # Person.find([1]) # returns an array for the object with ID = 1 # Person.where("administrator = 1").order("created_on DESC").find(1) # - # Note that returned records may not be in the same order as the ids you - # provide since database rows are unordered. Give an explicit <tt>order</tt> - # to ensure the results are sorted. + # <tt>ActiveRecord::RecordNotFound</tt> will be raised if one or more ids are not found. + # + # NOTE: The returned records may not be in the same order as the ids you + # provide since database rows are unordered. You'd need to provide an explicit <tt>order</tt> + # option if you want the results are sorted. # # ==== Find with lock # @@ -28,6 +30,34 @@ module ActiveRecord # person.visits += 1 # person.save! # end + # + # ==== Variations of +find+ + # + # Person.where(name: 'Spartacus', rating: 4) + # # returns a chainable list (which can be empty). + # + # Person.find_by(name: 'Spartacus', rating: 4) + # # returns the first item or nil. + # + # Person.where(name: 'Spartacus', rating: 4).first_or_initialize + # # returns the first item or returns a new instance (requires you call .save to persist against the database). + # + # Person.where(name: 'Spartacus', rating: 4).first_or_create + # # returns the first item or creates it and returns it, available since Rails 3.2.1. + # + # ==== Alternatives for +find+ + # + # Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none) + # # returns a boolean indicating if any record with the given conditions exist. + # + # Person.where(name: 'Spartacus', rating: 4).select("field1, field2, field3") + # # returns a chainable list of instances with only the mentioned fields. + # + # Person.where(name: 'Spartacus', rating: 4).ids + # # returns an Array of ids, available since Rails 3.2.1. + # + # Person.where(name: 'Spartacus', rating: 4).pluck(:field1, :field2) + # # returns an Array of the required fields, available since Rails 3.1. def find(*args) if block_given? to_a.find { |*block_args| yield(*block_args) } @@ -79,6 +109,19 @@ module ActiveRecord # Person.where(["user_name = :u", { u: user_name }]).first # Person.order("created_on DESC").offset(5).first # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3 + # + # ==== Rails 3 + # + # Person.first # SELECT "people".* FROM "people" LIMIT 1 + # + # NOTE: Rails 3 may not order this query by the primary key and the order + # will depend on the database implementation. In order to ensure that behavior, + # use <tt>User.order(:id).first</tt> instead. + # + # ==== Rails 4 + # + # Person.first # SELECT "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT 1 + # def first(limit = nil) if limit if order_values.empty? && primary_key diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index 6fe81e0d96..2f89699df7 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -797,26 +797,6 @@ module NestedAttributesOnACollectionAssociationTests end end - def test_validate_presence_of_parent_fails_without_inverse_of - Man.accepts_nested_attributes_for(:interests) - Man.reflect_on_association(:interests).options.delete(:inverse_of) - Man.reflect_on_association(:interests).clear_inverse_of_cache! - Interest.reflect_on_association(:man).options.delete(:inverse_of) - Interest.reflect_on_association(:man).clear_inverse_of_cache! - - repair_validations(Interest) do - Interest.validates_presence_of(:man) - assert_no_difference ['Man.count', 'Interest.count'] do - man = Man.create(:name => 'John', - :interests_attributes => [{:topic=>'Cars'}, {:topic=>'Sports'}]) - assert !man.errors[:"interests.man"].empty? - end - end - ensure - Man.reflect_on_association(:interests).options[:inverse_of] = :man - Interest.reflect_on_association(:man).options[:inverse_of] = :interests - end - def test_can_use_symbols_as_object_identifier @pirate.attributes = { :parrots_attributes => { :foo => { :name => 'Lovely Day' }, :bar => { :name => 'Blown Away' } } } assert_nothing_raised(NoMethodError) { @pirate.save! } diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index a96899ae10..af80b1ba42 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -85,7 +85,7 @@ class Author < ActiveRecord::Base has_many :author_favorites has_many :favorite_authors, -> { order('name') }, :through => :author_favorites - has_many :taggings, :through => :posts + has_many :taggings, :through => :posts, :source => :taggings has_many :taggings_2, :through => :posts, :source => :tagging has_many :tags, :through => :posts has_many :post_categories, :through => :posts, :source => :categories diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index dcda62e71d..b988184f34 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -141,7 +141,7 @@ class Client < Company belongs_to :firm_with_primary_key_symbols, :class_name => "Firm", :primary_key => :name, :foreign_key => :firm_name belongs_to :readonly_firm, -> { readonly }, :class_name => "Firm", :foreign_key => "firm_id" belongs_to :bob_firm, -> { where :name => "Bob" }, :class_name => "Firm", :foreign_key => "client_of" - has_many :accounts, :through => :firm + has_many :accounts, :through => :firm, :source => :accounts belongs_to :account class RaisedOnSave < RuntimeError; end |