diff options
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/CHANGELOG | 2 | ||||
-rw-r--r-- | activerecord/MIT-LICENSE | 2 | ||||
-rwxr-xr-x | activerecord/Rakefile | 2 | ||||
-rwxr-xr-x | activerecord/lib/active_record.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations.rb | 31 | ||||
-rwxr-xr-x | activerecord/lib/active_record/base.rb | 52 | ||||
-rw-r--r-- | activerecord/lib/active_record/dirty.rb | 16 | ||||
-rwxr-xr-x | activerecord/lib/active_record/fixtures.rb | 14 | ||||
-rw-r--r-- | activerecord/lib/active_record/version.rb | 2 | ||||
-rw-r--r-- | activerecord/test/cases/dirty_test.rb | 21 | ||||
-rw-r--r-- | activerecord/test/cases/finder_test.rb | 12 | ||||
-rwxr-xr-x | activerecord/test/cases/fixtures_test.rb | 20 | ||||
-rwxr-xr-x | activerecord/test/cases/inheritance_test.rb | 28 | ||||
-rwxr-xr-x | activerecord/test/models/company.rb | 4 | ||||
-rw-r--r-- | activerecord/test/models/pirate.rb | 2 |
15 files changed, 137 insertions, 73 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 4a130bf5c0..cd526a52a7 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,4 @@ -*SVN* +*2.1.0 RC1 (May 11th, 2008)* * Ensure hm:t preloading honours reflection options. Resolves #137. [Frederick Cheung] diff --git a/activerecord/MIT-LICENSE b/activerecord/MIT-LICENSE index 5fee6e106d..93be57f683 100644 --- a/activerecord/MIT-LICENSE +++ b/activerecord/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2007 David Heinemeier Hansson +Copyright (c) 2004-2008 David Heinemeier Hansson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/activerecord/Rakefile b/activerecord/Rakefile index d6033a9b85..043ab6d551 100755 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -171,7 +171,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.0.2' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.0.991' + PKG_BUILD) s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite" s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite" diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 8b274120df..d4f7170305 100755 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2007 David Heinemeier Hansson +# Copyright (c) 2004-2008 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index fb5f1f8a8c..c17e35f5e0 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1451,9 +1451,6 @@ module ActiveRecord join_dependency.joins_for_table_name(table) }.flatten.compact.uniq - - - is_distinct = !options[:joins].blank? || include_eager_conditions?(options, tables_from_conditions) || include_eager_order?(options, tables_from_order) sql = "SELECT " if is_distinct @@ -1500,26 +1497,28 @@ module ActiveRecord order.scan(/([\.\w]+).?\./).flatten end + def selects_tables(options) + select = options[:select] + return [] unless select && select.is_a?(String) + select.scan(/"?([\.\w]+)"?.?\./).flatten + end + # Checks if the conditions reference a table other than the current model table - def include_eager_conditions?(options,tables = nil) - tables = conditions_tables(options) - return false unless tables.any? - tables.any? do |condition_table_name| - condition_table_name != table_name - end + def include_eager_conditions?(options, tables = nil) + ((tables || conditions_tables(options)) - [table_name]).any? end # Checks if the query order references a table other than the current model's table. - def include_eager_order?(options,tables = nil) - tables = order_tables(options) - return false unless tables.any? - tables.any? do |order_table_name| - order_table_name != table_name - end + def include_eager_order?(options, tables = nil) + ((tables || order_tables(options)) - [table_name]).any? + end + + def include_eager_select?(options) + (selects_tables(options) - [table_name]).any? end def references_eager_loaded_tables?(options) - include_eager_order?(options) || include_eager_conditions?(options) + include_eager_order?(options) || include_eager_conditions?(options) || include_eager_select?(options) end def using_limitable_reflections?(reflections) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7999eec55d..5351f55200 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -438,7 +438,11 @@ module ActiveRecord #:nodoc: # adapters for, e.g., your development and test environments. cattr_accessor :schema_format , :instance_writer => false @@schema_format = :ruby - + + # Determine whether to store the full constant name including namespace when using STI + superclass_delegating_accessor :store_full_sti_class + self.store_full_sti_class = false + class << self # Class methods # Find operates with four different retrieval approaches: # @@ -522,7 +526,7 @@ module ActiveRecord #:nodoc: else find_from_ids(args, options) end end - + # This is an alias for find(:first). You can pass in all the same arguments to this method as you can # to find(:first) def first(*args) @@ -534,13 +538,13 @@ module ActiveRecord #:nodoc: def last(*args) find(:last, *args) end - + # This is an alias for find(:all). You can pass in all the same arguments to this method as you can # to find(:all) def all(*args) find(:all, *args) end - + # # Executes a custom sql query against your database and returns all the results. The results will # be returned as an array with columns requested encapsulated as attributes of the model you call @@ -587,10 +591,10 @@ module ActiveRecord #:nodoc: def exists?(id_or_conditions) connection.select_all( construct_finder_sql( - :select => "#{quoted_table_name}.#{primary_key}", - :conditions => expand_id_conditions(id_or_conditions), + :select => "#{quoted_table_name}.#{primary_key}", + :conditions => expand_id_conditions(id_or_conditions), :limit => 1 - ), + ), "#{name} Exists" ).size > 0 end @@ -616,7 +620,7 @@ module ActiveRecord #:nodoc: # # Creating an Array of new objects using a block, where the block is executed for each object: # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u| # u.is_admin = false - # end + # end def create(attributes = nil, &block) if attributes.is_a?(Array) attributes.collect { |attr| create(attr, &block) } @@ -1023,9 +1027,9 @@ module ActiveRecord #:nodoc: key = 'id' case primary_key_prefix_type when :table_name - key = Inflector.foreign_key(base_name, false) + key = base_name.to_s.foreign_key(false) when :table_name_with_underscore - key = Inflector.foreign_key(base_name) + key = base_name.to_s.foreign_key end key end @@ -1298,7 +1302,7 @@ module ActiveRecord #:nodoc: scoped_order = reverse_sql_order(scope(:find, :order)) scoped_methods.select { |s| s[:find].update(:order => scoped_order) } end - + find_initial(options.merge({ :order => order })) end @@ -1308,12 +1312,12 @@ module ActiveRecord #:nodoc: s.gsub!(/\s(asc|ASC)$/, ' DESC') elsif s.match(/\s(desc|DESC)$/) s.gsub!(/\s(desc|DESC)$/, ' ASC') - elsif !s.match(/\s(asc|ASC|desc|DESC)$/) + elsif !s.match(/\s(asc|ASC|desc|DESC)$/) s.concat(' DESC') end }.join(',') end - + def find_every(options) include_associations = merge_includes(scope(:find, :include), options[:include]) @@ -1557,8 +1561,8 @@ module ActiveRecord #:nodoc: def type_condition quoted_inheritance_column = connection.quote_column_name(inheritance_column) - type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{name.demodulize}' ") do |condition, subclass| - condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{subclass.name.demodulize}' " + type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{store_full_sti_class ? name : name.demodulize}' ") do |condition, subclass| + condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{store_full_sti_class ? subclass.name : subclass.name.demodulize}' " end " (#{type_condition}) " @@ -1566,8 +1570,8 @@ module ActiveRecord #:nodoc: # Guesses the table name, but does not decorate it with prefix and suffix information. def undecorated_table_name(class_name = base_class.name) - table_name = Inflector.underscore(Inflector.demodulize(class_name)) - table_name = Inflector.pluralize(table_name) if pluralize_table_names + table_name = class_name.to_s.demodulize.underscore + table_name = table_name.pluralize if pluralize_table_names table_name end @@ -1616,7 +1620,7 @@ module ActiveRecord #:nodoc: self.class_eval %{ def self.#{method_id}(*args) guard_protected_attributes = false - + if args[0].is_a?(Hash) guard_protected_attributes = true attributes = args[0].with_indifferent_access @@ -1629,7 +1633,7 @@ module ActiveRecord #:nodoc: set_readonly_option!(options) record = find_initial(options) - + if record.nil? record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) } #{'yield(record) if block_given?'} @@ -2129,14 +2133,14 @@ module ActiveRecord #:nodoc: # We can't use alias_method here, because method 'id' optimizes itself on the fly. (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes end - + # Returns a cache key that can be used to identify this record. Examples: # # Product.new.cache_key # => "products/new" # Product.find(5).cache_key # => "products/5" (updated_at not available) # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available) def cache_key - case + case when new_record? "#{self.class.name.tableize}/new" when self[:updated_at] @@ -2170,7 +2174,7 @@ module ActiveRecord #:nodoc: # Note: If your model specifies any validations then the method declaration dynamically # changes to: # save(perform_validation=true) - # Calling save(false) saves the model without running validations. + # Calling save(false) saves the model without running validations. # See ActiveRecord::Validations for more information. def save create_or_update @@ -2342,7 +2346,7 @@ module ActiveRecord #:nodoc: # Returns a hash of all the attributes with their names as keys and the values of the attributes as values. - def attributes(options = nil) + def attributes self.attribute_names.inject({}) do |attrs, name| attrs[name] = read_attribute(name) attrs @@ -2492,7 +2496,7 @@ module ActiveRecord #:nodoc: # Message class in that example. def ensure_proper_type unless self.class.descends_from_active_record? - write_attribute(self.class.inheritance_column, Inflector.demodulize(self.class.name)) + write_attribute(self.class.inheritance_column, store_full_sti_class ? self.class.name : self.class.name.demodulize) end end diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index c6d89e3a05..6034963811 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -69,19 +69,19 @@ module ActiveRecord changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h } end - - # Clear changed attributes after they are saved. + # Attempts to +save+ the record and clears changed attributes if successful. def save_with_dirty(*args) #:nodoc: - save_without_dirty(*args) - ensure - changed_attributes.clear + if status = save_without_dirty(*args) + changed_attributes.clear + end + status end - # Clear changed attributes after they are saved. + # Attempts to <tt>save!</tt> the record and clears changed attributes if successful. def save_with_dirty!(*args) #:nodoc: - save_without_dirty!(*args) - ensure + status = save_without_dirty!(*args) changed_attributes.clear + status end private diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 9367ea523d..ac06cdbe43 100755 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -197,20 +197,20 @@ end # class FooTest < ActiveSupport::TestCase # self.use_transactional_fixtures = true # self.use_instantiated_fixtures = false -# +# # fixtures :foos -# +# # def test_godzilla # assert !Foo.find(:all).empty? # Foo.destroy_all # assert Foo.find(:all).empty? # end -# +# # def test_godzilla_aftermath # assert !Foo.find(:all).empty? # end # end -# +# # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures, # then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes. # @@ -730,7 +730,7 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) reader.each do |row| data = {} row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip } - self["#{Inflector::underscore(@class_name)}_#{i+=1}"] = Fixture.new(data, model_class) + self["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class) end end @@ -854,14 +854,14 @@ module Test #:nodoc: require_dependency file_name rescue LoadError => e # Let's hope the developer has included it himself - + # Let's warn in case this is a subdependency, otherwise # subdependency error messages are totally cryptic if ActiveRecord::Base.logger ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}") end end - + def require_fixture_classes(table_names = nil) (table_names || fixture_table_names).each do |table_name| file_name = table_name.to_s diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index a8ee7dbeb9..1463e84764 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -2,7 +2,7 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 MINOR = 0 - TINY = 2 + TINY = 991 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 7412e63872..1266eb5036 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -78,7 +78,7 @@ class DirtyTest < ActiveRecord::TestCase end def test_association_assignment_changes_foreign_key - pirate = Pirate.create! + pirate = Pirate.create!(:catchphrase => 'jarl') pirate.parrot = Parrot.create! assert pirate.changed? assert_equal %w(parrot_id), pirate.changed @@ -115,6 +115,18 @@ class DirtyTest < ActiveRecord::TestCase end end + def test_changed_attributes_should_be_preserved_if_save_failure + pirate = Pirate.new + pirate.parrot_id = 1 + assert !pirate.save + check_pirate_after_save_failure(pirate) + + pirate = Pirate.new + pirate.parrot_id = 1 + assert_raises(ActiveRecord::RecordInvalid) { pirate.save! } + check_pirate_after_save_failure(pirate) + end + private def with_partial_updates(klass, on = true) old = klass.partial_updates? @@ -123,4 +135,11 @@ class DirtyTest < ActiveRecord::TestCase ensure klass.partial_updates = old end + + def check_pirate_after_save_failure(pirate) + assert pirate.changed? + assert pirate.parrot_id_changed? + assert_equal %w(parrot_id), pirate.changed + assert_nil pirate.parrot_id_was + end end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 2acfe9b387..5c0f0e2ef1 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -9,6 +9,7 @@ require 'models/developer' require 'models/post' require 'models/customer' require 'models/job' +require 'models/categorization' class FinderTest < ActiveRecord::TestCase fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers @@ -859,12 +860,17 @@ class FinderTest < ActiveRecord::TestCase end def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct - assert_equal 2, Post.find(:all,:include=>{:authors=>:author_address},:order=>' author_addresses.id DESC ', :limit=>2).size + assert_equal 2, Post.find(:all, :include => { :authors => :author_address }, :order => ' author_addresses.id DESC ', :limit => 2).size - assert_equal 3, Post.find(:all,:include=>{:author=>:author_address,:authors=>:author_address}, - :order=>' author_addresses_authors.id DESC ', :limit=>3).size + assert_equal 3, Post.find(:all, :include => { :author => :author_address, :authors => :author_address}, + :order => ' author_addresses_authors.id DESC ', :limit => 3).size end + def test_with_limiting_with_custom_select + posts = Post.find(:all, :include => :author, :select => ' posts.*, authors.id as "author_id"', :limit => 3) + assert_equal 3, posts.size + assert_equal [0, 1, 1], posts.map(&:author_id).sort + end protected def bind(statement, *vars) diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 2787b9a39d..aca7cfb367 100755 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -610,15 +610,17 @@ class ActiveSupportSubclassWithFixturesTest < ActiveRecord::TestCase end class FixtureLoadingTest < ActiveRecord::TestCase - def test_logs_message_for_failed_dependency_load - Test::Unit::TestCase.expects(:require_dependency).with(:does_not_exist).raises(LoadError) - ActiveRecord::Base.logger.expects(:warn) - Test::Unit::TestCase.try_to_load_dependency(:does_not_exist) - end + uses_mocha 'reloading_fixtures_through_accessor_methods' do + def test_logs_message_for_failed_dependency_load + Test::Unit::TestCase.expects(:require_dependency).with(:does_not_exist).raises(LoadError) + ActiveRecord::Base.logger.expects(:warn) + Test::Unit::TestCase.try_to_load_dependency(:does_not_exist) + end - def test_does_not_logs_message_for_successful_dependency_load - Test::Unit::TestCase.expects(:require_dependency).with(:works_out_fine) - ActiveRecord::Base.logger.expects(:warn).never - Test::Unit::TestCase.try_to_load_dependency(:works_out_fine) + def test_does_not_logs_message_for_successful_dependency_load + Test::Unit::TestCase.expects(:require_dependency).with(:works_out_fine) + ActiveRecord::Base.logger.expects(:warn).never + Test::Unit::TestCase.try_to_load_dependency(:works_out_fine) + end end end diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index c9eb83e371..27394924a1 100755 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -5,6 +5,34 @@ require 'models/subscriber' class InheritanceTest < ActiveRecord::TestCase fixtures :companies, :projects, :subscribers, :accounts + + def test_should_store_demodulized_class_name_with_store_full_sti_class_option_disabled + old = ActiveRecord::Base.store_full_sti_class + ActiveRecord::Base.store_full_sti_class = false + item = Namespaced::Company.new + assert_equal 'Company', item[:type] + ensure + ActiveRecord::Base.store_full_sti_class = old + end + + def test_should_store_full_class_name_with_store_full_sti_class_option_enabled + old = ActiveRecord::Base.store_full_sti_class + ActiveRecord::Base.store_full_sti_class = true + item = Namespaced::Company.new + assert_equal 'Namespaced::Company', item[:type] + ensure + ActiveRecord::Base.store_full_sti_class = old + end + + def test_different_namespace_subclass_should_load_correctly_with_store_full_sti_class_option + old = ActiveRecord::Base.store_full_sti_class + ActiveRecord::Base.store_full_sti_class = true + item = Namespaced::Company.create :name => "Wolverine 2" + assert_not_nil Company.find(item.id) + assert_not_nil Namespaced::Company.find(item.id) + ensure + ActiveRecord::Base.store_full_sti_class = old + end def test_company_descends_from_active_record assert_raise(NoMethodError) { ActiveRecord::Base.descends_from_active_record? } diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 3d76dfd398..f637490c59 100755 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -15,6 +15,10 @@ class Company < AbstractCompany end end +module Namespaced + class Company < ::Company + end +end class Firm < Company has_many :clients, :order => "id", :dependent => :destroy, :counter_sql => diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index bb4d02c10f..51c8183dee 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -4,4 +4,6 @@ class Pirate < ActiveRecord::Base has_many :treasures, :as => :looter has_many :treasure_estimates, :through => :treasures, :source => :price_estimates + + validates_presence_of :catchphrase end |