diff options
Diffstat (limited to 'activerecord')
27 files changed, 157 insertions, 53 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 4e33dfe69f..b9039ce996 100644..100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1679,19 +1679,19 @@ module ActiveRecord else all << cond end end - conditions.join(' ').scan(/([\.\w]+).?\./).flatten + conditions.join(' ').scan(/([\.a-zA-Z_]+).?\./).flatten end def order_tables(options) order = [options[:order], scope(:find, :order) ].join(", ") return [] unless order && order.is_a?(String) - order.scan(/([\.\w]+).?\./).flatten + order.scan(/([\.a-zA-Z_]+).?\./).flatten end def selects_tables(options) select = options[:select] return [] unless select && select.is_a?(String) - select.scan(/"?([\.\w]+)"?.?\./).flatten + select.scan(/"?([\.a-zA-Z_]+)"?.?\./).flatten end # Checks if the conditions reference a table other than the current model table @@ -2099,10 +2099,8 @@ module ActiveRecord else "" end || '' - join << %(AND %s.%s = %s ) % [ - connection.quote_table_name(aliased_table_name), - connection.quote_column_name(klass.inheritance_column), - klass.quote_value(klass.sti_name)] unless klass.descends_from_active_record? + join << %(AND %s) % [ + klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record? [through_reflection, reflection].each do |ref| join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions] diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 77fc827e11..981be3b1a9 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -131,10 +131,6 @@ module ActiveRecord records.map { |record| record.quoted_id }.join(',') end - def interpolate_sql_options!(options, *keys) - keys.each { |key| options[key] &&= interpolate_sql(options[key]) } - end - def interpolate_sql(sql, record = nil) @owner.send(:interpolate_sql, sql, record) end diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index d516d54151..e7e433b6b6 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -70,10 +70,8 @@ module ActiveRecord end def construct_sql - interpolate_sql_options!(@reflection.options, :finder_sql) - if @reflection.options[:finder_sql] - @finder_sql = @reflection.options[:finder_sql] + @finder_sql = interpolate_sql(@reflection.options[:finder_sql]) else @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{owner_quoted_id} " @finder_sql << " AND (#{conditions})" if conditions diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index e6fa15c173..ce62127505 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -35,8 +35,11 @@ module ActiveRecord else @reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include]) end - - @target = [] and loaded if count == 0 + + # If there's nothing in the database and @target has no new records + # we are certain the current target is an empty array. This is a + # documented side-effect of the method that may avoid an extra SELECT. + @target ||= [] and loaded if count == 0 if @reflection.options[:limit] count = [ @reflection.options[:limit], count ].min diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index e1bfff5923..24b02efc35 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -237,7 +237,7 @@ module ActiveRecord end def build_sti_condition - "#{@reflection.through_reflection.quoted_table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.sti_name)}" + @reflection.through_reflection.klass.send(:type_condition) end alias_method :sql_conditions, :conditions diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index fdc0fa52c9..18733255d2 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -21,8 +21,8 @@ module ActiveRecord def replace(obj, dont_save = false) load_target - unless @target.nil? - if dependent? && !dont_save && @target != obj + unless @target.nil? || @target == obj + if dependent? && !dont_save @target.destroy unless @target.new_record? @owner.clear_association_cache else diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index edab017d26..1658cccb16 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -612,7 +612,7 @@ module ActiveRecord #:nodoc: # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date] # > [#<Post:0x36bff9c @attributes={"first_name"=>"The Cheap Man Buys Twice"}>, ...] def find_by_sql(sql) - connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) } + connection.select_all(sanitize_sql(sql), "#{name} Load").map { |record| instantiate(record) } end # Checks whether a record exists in the database that matches conditions given. These conditions @@ -1644,10 +1644,11 @@ module ActiveRecord #:nodoc: sql << "WHERE #{merged_conditions} " unless merged_conditions.blank? end - def type_condition + def type_condition(table_alias=nil) + quoted_table_alias = self.connection.quote_table_name(table_alias || table_name) quoted_inheritance_column = connection.quote_column_name(inheritance_column) - type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass| - condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{subclass.sti_name}' " + type_condition = subclasses.inject("#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass| + condition << "OR #{quoted_table_alias}.#{quoted_inheritance_column} = '#{subclass.sti_name}' " end " (#{type_condition}) " @@ -1784,7 +1785,7 @@ module ActiveRecord #:nodoc: def attribute_condition(argument) case argument when nil then "IS ?" - when Array, ActiveRecord::Associations::AssociationCollection then "IN (?)" + when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)" when Range then "BETWEEN ? AND ?" else "= ?" end @@ -2629,7 +2630,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 @@ -2644,6 +2645,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 ] @@ -2657,8 +2662,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 2ca1a0aaa3..246f87b7a9 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/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 2afd6064ad..81a2e56b34 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -72,21 +72,12 @@ module ActiveRecord private def cache_sql(sql) - result = - if @query_cache.has_key?(sql) - log_info(sql, "CACHE", 0.0) - @query_cache[sql] - else - @query_cache[sql] = yield - end - - if Array === result - result.collect { |row| row.dup } + if @query_cache.has_key?(sql) + log_info(sql, "CACHE", 0.0) + @query_cache[sql] else - result.duplicable? ? result.dup : result + @query_cache[sql] = yield.freeze end - rescue TypeError - result end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 856435517a..74da0d9c85 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -761,7 +761,8 @@ module ActiveRecord begin execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - rescue ActiveRecord::StatementInvalid + rescue ActiveRecord::StatementInvalid => e + raise e if postgresql_version > 80000 # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it. begin begin_db_transaction 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/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index d5a1c5fe08..c99c4beca9 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, include_private = false) + super || @proxy_scope.respond_to?(method, include_private) + end + def any? if block_given? proxy_found.any? { |*block_args| yield(*block_args) } diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 3f74c03714..935b1939d8 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -109,7 +109,7 @@ module ActiveRecord # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute, # and +other_aggregation+ has an options hash assigned to it. def ==(other_aggregation) - name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record + other_aggregation.kind_of?(self.class) && name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record end def sanitized_conditions #:nodoc: diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 1ea520f3b6..8fe4336bbc 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -650,12 +650,11 @@ module ActiveRecord if value.nil? comparison_operator = "IS ?" - else + elsif is_text_column comparison_operator = "#{connection.case_sensitive_equality_operator} ?" - - if is_text_column - value = value.to_s - end + value = value.to_s + else + comparison_operator = "= ?" end sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}" diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index 1f8a1090eb..8c9ae8a031 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -68,6 +68,18 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end end + def test_eager_association_loading_with_has_many_sti_and_subclasses + silly = SillyReply.new(:title => "gaga", :content => "boo-boo", :parent_id => 1) + silly.parent_id = 1 + assert silly.save + + topics = Topic.find(:all, :include => :replies, :order => 'topics.id, replies_topics.id') + assert_no_queries do + assert_equal 2, topics[0].replies.size + assert_equal 0, topics[1].replies.size + end + end + def test_eager_association_loading_with_belongs_to_sti replies = Reply.find(:all, :include => :topic, :order => 'topics.id') assert replies.include?(topics(:second)) diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 58506574f8..f37e18df35 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -559,6 +559,13 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_nothing_raised { Post.find(:all, :include => 'comments') } end + def test_eager_with_floating_point_numbers + assert_queries(2) do + # Before changes, the floating point numbers will be interpreted as table names and will cause this to run in one query + Comment.find :all, :conditions => "123.456 = 123.456", :include => :post + end + end + def test_preconfigured_includes_with_belongs_to author = posts(:welcome).author_with_posts assert_no_queries {assert_equal 5, author.posts.size} 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 f71b122ff0..dfd82534ff 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 @@ -450,6 +450,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find" end + def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations + # interpolate once: + assert_equal [developers(:david), developers(:poor_jamis), developers(:jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation" + # interpolate again, for a different project id + assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation" + end + def test_find_in_association_with_custom_finder_sql_and_string_id assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find" end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index b806e885e1..da3c8fb28e 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -395,6 +395,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 3, company.clients_of_firm.size end + def test_collection_size_twice_for_regressions + post = posts(:thinking) + assert_equal 0, post.readers.size + # This test needs a post that has no readers, we assert it to ensure it holds, + # but need to reload the post because the very call to #size hides the bug. + post.reload + post.readers.build + size1 = post.readers.size + size2 = post.readers.size + assert_equal size1, size2 + end + def test_build_many company = companies(:first_firm) new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) } diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 99639849a5..ec06be5eba 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -79,6 +79,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_raises(ActiveRecord::RecordNotFound) { Account.find(old_account_id) } end + def test_natural_assignment_to_already_associated_record + company = companies(:first_firm) + account = accounts(:signals37) + assert_equal company.account, account + company.account = account + company.reload + account.reload + assert_equal company.account, account + end + def test_assignment_without_replacement apple = Firm.create("name" => "Apple") citibank = Account.create("credit_limit" => 10) diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 9e79d9c8a1..7a0427aabc 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -694,6 +694,13 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase assert ! david.categories.include?(category) end + def test_has_many_through_goes_through_all_sti_classes + sub_sti_post = SubStiPost.create!(:title => 'test', :body => 'test', :author_id => 1) + new_comment = sub_sti_post.comments.create(:body => 'test') + + assert_equal [9, 10, new_comment.id], authors(:david).sti_post_comments.map(&:id).sort + end + private # create dynamic Post models to allow different dependency options def find_post_with_dependency(post_id, association, association_name, dependency) diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 0f9eda4d09..36d30ade5e 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -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/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 7bd712e11e..6f6ea1cbe9 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -4,6 +4,7 @@ require 'models/topic' require 'models/comment' require 'models/reply' require 'models/author' +require 'models/developer' class NamedScopeTest < ActiveRecord::TestCase fixtures :posts, :authors, :topics, :comments, :author_addresses @@ -45,6 +46,17 @@ 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_respond_to_respects_include_private_parameter + assert !Topic.approved.respond_to?(:load_found) + assert Topic.approved.respond_to?(:load_found, true) + end + def test_subclasses_inherit_scopes assert Topic.scopes.include?(:base) @@ -232,4 +244,12 @@ class NamedScopeTest < ActiveRecord::TestCase assert topic.approved assert_equal 'lifo', topic.author_name end + + def test_find_all_should_behave_like_select + assert_equal Topic.base.select(&:approved), Topic.base.find_all(&:approved) + end + + def test_should_use_where_in_query_for_named_scope + assert_equal Developer.find_all_by_name('Jamis'), Developer.find_all_by_id(Developer.jamises) + end end diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 723062e3b8..4b86e32dbf 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -166,6 +166,10 @@ class ReflectionTest < ActiveRecord::TestCase assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size end + def test_reflection_should_not_raise_error_when_compared_to_other_object + assert_nothing_raised { Firm.reflections[:clients] == Object.new } + end + private def assert_reflection(klass, association, options) assert reflection = klass.reflect_on_association(association) diff --git a/activerecord/test/connections/native_mysql/connection.rb b/activerecord/test/connections/native_mysql/connection.rb index 1fab444e58..140e06d631 100644 --- a/activerecord/test/connections/native_mysql/connection.rb +++ b/activerecord/test/connections/native_mysql/connection.rb @@ -2,9 +2,7 @@ print "Using native MySQL\n" require_dependency 'models/course' require 'logger' -RAILS_DEFAULT_LOGGER = Logger.new('debug.log') -RAILS_DEFAULT_LOGGER.level = Logger::DEBUG -ActiveRecord::Base.logger = RAILS_DEFAULT_LOGGER +ActiveRecord::Base.logger = Logger.new("debug.log") # GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost'; # GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost'; diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 136dc39cf3..c6aa0293c2 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -32,6 +32,9 @@ class Author < ActiveRecord::Base has_many :special_posts has_many :special_post_comments, :through => :special_posts, :source => :comments + has_many :sti_posts, :class_name => 'StiPost' + has_many :sti_post_comments, :through => :sti_posts, :source => :comments + has_many :special_nonexistant_posts, :class_name => "SpecialPost", :conditions => "posts.body = 'nonexistant'" has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => "comments.post_id = 0" has_many :nonexistant_comments, :through => :posts diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 9f26cacdec..c08476f728 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -43,6 +43,8 @@ class Developer < ActiveRecord::Base has_many :audit_logs + named_scope :jamises, :conditions => {:name => 'Jamis'} + validates_inclusion_of :salary, :in => 50000..200000 validates_length_of :name, :within => 3..20 |