aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2009-09-21 21:14:04 +0100
committerPratik Naik <pratiknaik@gmail.com>2009-09-21 21:14:04 +0100
commit340be9bddd8e5902e0218a0101a40a17a4afd558 (patch)
treeef4de25f3f8eb610dc2235f0762b01cb1d464efd /activerecord
parentb31cdb55422226cd45a2234a4b54986f1f611151 (diff)
parent1bbb9b2db05730194edfd7d2cef9f5fcb9d79e50 (diff)
downloadrails-340be9bddd8e5902e0218a0101a40a17a4afd558.tar.gz
rails-340be9bddd8e5902e0218a0101a40a17a4afd558.tar.bz2
rails-340be9bddd8e5902e0218a0101a40a17a4afd558.zip
Merge commit 'mainstream/master'
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/Rakefile7
-rw-r--r--activerecord/activerecord.gemspec37
-rwxr-xr-xactiverecord/lib/active_record/associations.rb62
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb4
-rw-r--r--activerecord/lib/active_record/autosave_association.rb52
-rwxr-xr-xactiverecord/lib/active_record/base.rb61
-rw-r--r--activerecord/lib/active_record/callbacks.rb222
-rwxr-xr-xactiverecord/lib/active_record/connection_adapters/abstract_adapter.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb8
-rw-r--r--activerecord/lib/active_record/locking/pessimistic.rb22
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb60
-rw-r--r--activerecord/lib/active_record/observer.rb20
-rw-r--r--activerecord/lib/active_record/validations.rb35
-rw-r--r--activerecord/lib/activerecord.rb1
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb9
-rw-r--r--activerecord/test/cases/autosave_association_test.rb221
-rw-r--r--activerecord/test/cases/callbacks_observers_test.rb3
-rw-r--r--activerecord/test/cases/callbacks_test.rb114
-rw-r--r--activerecord/test/cases/class_inheritable_attributes_test.rb32
-rw-r--r--activerecord/test/cases/connection_test_mysql.rb8
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/lifecycle_test.rb37
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb80
-rw-r--r--activerecord/test/cases/reflection_test.rb6
-rw-r--r--activerecord/test/cases/repair_helper.rb46
-rw-r--r--activerecord/test/cases/transactions_test.rb12
-rw-r--r--activerecord/test/cases/validations/i18n_generate_message_validation_test.rb12
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb24
-rw-r--r--activerecord/test/cases/validations_test.rb18
-rw-r--r--activerecord/test/fixtures/accounts.yml1
-rw-r--r--activerecord/test/models/author.rb3
-rw-r--r--activerecord/test/models/company.rb11
-rw-r--r--activerecord/test/models/pirate.rb9
-rw-r--r--activerecord/test/models/project.rb3
-rw-r--r--activerecord/test/models/reply.rb8
-rw-r--r--activerecord/test/models/ship.rb2
-rw-r--r--activerecord/test/models/topic.rb19
-rw-r--r--activerecord/test/models/treasure.rb2
-rw-r--r--activerecord/test/schema/mysql_specific_schema.rb12
-rw-r--r--activerecord/test/schema/schema.rb1
41 files changed, 735 insertions, 575 deletions
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index a9e4a92e37..aa2c998d07 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -208,6 +208,7 @@ spec = Gem::Specification.new do |s|
end
s.add_dependency('activesupport', '= 3.0.pre' + PKG_BUILD)
+ s.add_dependency('activemodel', '= 3.0.pre' + PKG_BUILD)
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
@@ -232,6 +233,12 @@ Rake::GemPackageTask.new(spec) do |p|
p.need_zip = true
end
+task :gemspec do
+ File.open(File.join(File.dirname(__FILE__), "#{spec.name}.gemspec"), "w") do |file|
+ file.puts spec.to_ruby
+ end
+end
+
task :lines do
lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
new file mode 100644
index 0000000000..af61f18fe8
--- /dev/null
+++ b/activerecord/activerecord.gemspec
@@ -0,0 +1,37 @@
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{activerecord}
+ s.version = "3.0.pre"
+
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
+ s.authors = ["David Heinemeier Hansson"]
+ s.autorequire = %q{active_record}
+ s.date = %q{2009-09-01}
+ s.description = %q{Implements the ActiveRecord pattern (Fowler, PoEAA) for ORM. It ties database tables and classes together for business objects, like Customer or Subscription, that can find, save, and destroy themselves without resorting to manual SQL.}
+ s.email = %q{david@loudthinking.com}
+ s.extra_rdoc_files = ["README"]
+ s.files = ["Rakefile", "install.rb", "README", "RUNNING_UNIT_TESTS", "CHANGELOG", "lib/active_record", "lib/active_record/aggregations.rb", "lib/active_record/association_preload.rb", "lib/active_record/associations", "lib/active_record/associations/association_collection.rb", "lib/active_record/associations/association_proxy.rb", "lib/active_record/associations/belongs_to_association.rb", "lib/active_record/associations/belongs_to_polymorphic_association.rb", "lib/active_record/associations/has_and_belongs_to_many_association.rb", "lib/active_record/associations/has_many_association.rb", "lib/active_record/associations/has_many_through_association.rb", "lib/active_record/associations/has_one_association.rb", "lib/active_record/associations/has_one_through_association.rb", "lib/active_record/associations/through_association_scope.rb", "lib/active_record/associations.rb", "lib/active_record/attribute_methods", "lib/active_record/attribute_methods/before_type_cast.rb", "lib/active_record/attribute_methods/dirty.rb", "lib/active_record/attribute_methods/primary_key.rb", "lib/active_record/attribute_methods/query.rb", "lib/active_record/attribute_methods/read.rb", "lib/active_record/attribute_methods/time_zone_conversion.rb", "lib/active_record/attribute_methods/write.rb", "lib/active_record/attribute_methods.rb", "lib/active_record/autosave_association.rb", "lib/active_record/base.rb", "lib/active_record/batches.rb", "lib/active_record/calculations.rb", "lib/active_record/callbacks.rb", "lib/active_record/connection_adapters", "lib/active_record/connection_adapters/abstract", "lib/active_record/connection_adapters/abstract/connection_pool.rb", "lib/active_record/connection_adapters/abstract/connection_specification.rb", "lib/active_record/connection_adapters/abstract/database_statements.rb", "lib/active_record/connection_adapters/abstract/query_cache.rb", "lib/active_record/connection_adapters/abstract/quoting.rb", "lib/active_record/connection_adapters/abstract/schema_definitions.rb", "lib/active_record/connection_adapters/abstract/schema_statements.rb", "lib/active_record/connection_adapters/abstract_adapter.rb", "lib/active_record/connection_adapters/mysql_adapter.rb", "lib/active_record/connection_adapters/postgresql_adapter.rb", "lib/active_record/connection_adapters/sqlite3_adapter.rb", "lib/active_record/connection_adapters/sqlite_adapter.rb", "lib/active_record/dynamic_finder_match.rb", "lib/active_record/dynamic_scope_match.rb", "lib/active_record/fixtures.rb", "lib/active_record/locale", "lib/active_record/locale/en.yml", "lib/active_record/locking", "lib/active_record/locking/optimistic.rb", "lib/active_record/locking/pessimistic.rb", "lib/active_record/migration.rb", "lib/active_record/named_scope.rb", "lib/active_record/nested_attributes.rb", "lib/active_record/observer.rb", "lib/active_record/query_cache.rb", "lib/active_record/reflection.rb", "lib/active_record/schema.rb", "lib/active_record/schema_dumper.rb", "lib/active_record/serialization.rb", "lib/active_record/serializers", "lib/active_record/serializers/xml_serializer.rb", "lib/active_record/session_store.rb", "lib/active_record/state_machine.rb", "lib/active_record/test_case.rb", "lib/active_record/timestamp.rb", "lib/active_record/transactions.rb", "lib/active_record/validations", "lib/active_record/validations/associated.rb", "lib/active_record/validations/uniqueness.rb", "lib/active_record/validations.rb", "lib/active_record/validator.rb", "lib/active_record/version.rb", "lib/active_record.rb", "lib/activerecord.rb", "test/assets", "test/assets/example.log", "test/assets/flowers.jpg", "test/cases", "test/cases/active_schema_test_mysql.rb", "test/cases/active_schema_test_postgresql.rb", "test/cases/adapter_test.rb", "test/cases/aggregations_test.rb", "test/cases/ar_schema_test.rb", "test/cases/associations", "test/cases/associations/belongs_to_associations_test.rb", "test/cases/associations/callbacks_test.rb", "test/cases/associations/cascaded_eager_loading_test.rb", "test/cases/associations/eager_load_includes_full_sti_class_test.rb", "test/cases/associations/eager_load_nested_include_test.rb", "test/cases/associations/eager_singularization_test.rb", "test/cases/associations/eager_test.rb", "test/cases/associations/extension_test.rb", "test/cases/associations/habtm_join_table_test.rb", "test/cases/associations/has_and_belongs_to_many_associations_test.rb", "test/cases/associations/has_many_associations_test.rb", "test/cases/associations/has_many_through_associations_test.rb", "test/cases/associations/has_one_associations_test.rb", "test/cases/associations/has_one_through_associations_test.rb", "test/cases/associations/inner_join_association_test.rb", "test/cases/associations/inverse_associations_test.rb", "test/cases/associations/join_model_test.rb", "test/cases/associations_test.rb", "test/cases/attribute_methods_test.rb", "test/cases/autosave_association_test.rb", "test/cases/base_test.rb", "test/cases/batches_test.rb", "test/cases/binary_test.rb", "test/cases/calculations_test.rb", "test/cases/callbacks_observers_test.rb", "test/cases/callbacks_test.rb", "test/cases/class_inheritable_attributes_test.rb", "test/cases/column_alias_test.rb", "test/cases/column_definition_test.rb", "test/cases/connection_pool_test.rb", "test/cases/connection_test_firebird.rb", "test/cases/connection_test_mysql.rb", "test/cases/copy_table_test_sqlite.rb", "test/cases/database_statements_test.rb", "test/cases/datatype_test_postgresql.rb", "test/cases/date_time_test.rb", "test/cases/default_test_firebird.rb", "test/cases/defaults_test.rb", "test/cases/deprecated_finder_test.rb", "test/cases/dirty_test.rb", "test/cases/finder_respond_to_test.rb", "test/cases/finder_test.rb", "test/cases/fixtures_test.rb", "test/cases/helper.rb", "test/cases/i18n_test.rb", "test/cases/inheritance_test.rb", "test/cases/invalid_date_test.rb", "test/cases/json_serialization_test.rb", "test/cases/lifecycle_test.rb", "test/cases/locking_test.rb", "test/cases/method_scoping_test.rb", "test/cases/migration_test.rb", "test/cases/migration_test_firebird.rb", "test/cases/mixin_test.rb", "test/cases/modules_test.rb", "test/cases/multiple_db_test.rb", "test/cases/named_scope_test.rb", "test/cases/nested_attributes_test.rb", "test/cases/pk_test.rb", "test/cases/pooled_connections_test.rb", "test/cases/query_cache_test.rb", "test/cases/readonly_test.rb", "test/cases/reflection_test.rb", "test/cases/reload_models_test.rb", "test/cases/repair_helper.rb", "test/cases/reserved_word_test_mysql.rb", "test/cases/sanitize_test.rb", "test/cases/schema_authorization_test_postgresql.rb", "test/cases/schema_dumper_test.rb", "test/cases/schema_test_postgresql.rb", "test/cases/serialization_test.rb", "test/cases/state_machine_test.rb", "test/cases/synonym_test_oracle.rb", "test/cases/timestamp_test.rb", "test/cases/transactions_test.rb", "test/cases/unconnected_test.rb", "test/cases/validations", "test/cases/validations/association_validation_test.rb", "test/cases/validations/i18n_generate_message_validation_test.rb", "test/cases/validations/i18n_validation_test.rb", "test/cases/validations/uniqueness_validation_test.rb", "test/cases/validations_test.rb", "test/cases/xml_serialization_test.rb", "test/config.rb", "test/connections", "test/connections/jdbc_jdbcderby", "test/connections/jdbc_jdbcderby/connection.rb", "test/connections/jdbc_jdbch2", "test/connections/jdbc_jdbch2/connection.rb", "test/connections/jdbc_jdbchsqldb", "test/connections/jdbc_jdbchsqldb/connection.rb", "test/connections/jdbc_jdbcmysql", "test/connections/jdbc_jdbcmysql/connection.rb", "test/connections/jdbc_jdbcpostgresql", "test/connections/jdbc_jdbcpostgresql/connection.rb", "test/connections/jdbc_jdbcsqlite3", "test/connections/jdbc_jdbcsqlite3/connection.rb", "test/connections/native_db2", "test/connections/native_db2/connection.rb", "test/connections/native_firebird", "test/connections/native_firebird/connection.rb", "test/connections/native_frontbase", "test/connections/native_frontbase/connection.rb", "test/connections/native_mysql", "test/connections/native_mysql/connection.rb", "test/connections/native_openbase", "test/connections/native_openbase/connection.rb", "test/connections/native_oracle", "test/connections/native_oracle/connection.rb", "test/connections/native_postgresql", "test/connections/native_postgresql/connection.rb", "test/connections/native_sqlite3", "test/connections/native_sqlite3/connection.rb", "test/connections/native_sqlite3/in_memory_connection.rb", "test/connections/native_sybase", "test/connections/native_sybase/connection.rb", "test/fixtures", "test/fixtures/accounts.yml", "test/fixtures/all", "test/fixtures/all/developers.yml", "test/fixtures/all/people.csv", "test/fixtures/all/tasks.yml", "test/fixtures/author_addresses.yml", "test/fixtures/author_favorites.yml", "test/fixtures/authors.yml", "test/fixtures/binaries.yml", "test/fixtures/books.yml", "test/fixtures/categories", "test/fixtures/categories/special_categories.yml", "test/fixtures/categories/subsubdir", "test/fixtures/categories/subsubdir/arbitrary_filename.yml", "test/fixtures/categories.yml", "test/fixtures/categories_ordered.yml", "test/fixtures/categories_posts.yml", "test/fixtures/categorizations.yml", "test/fixtures/clubs.yml", "test/fixtures/comments.yml", "test/fixtures/companies.yml", "test/fixtures/computers.yml", "test/fixtures/courses.yml", "test/fixtures/customers.yml", "test/fixtures/developers.yml", "test/fixtures/developers_projects.yml", "test/fixtures/edges.yml", "test/fixtures/entrants.yml", "test/fixtures/faces.yml", "test/fixtures/fixture_database.sqlite3", "test/fixtures/fixture_database_2.sqlite3", "test/fixtures/fk_test_has_fk.yml", "test/fixtures/fk_test_has_pk.yml", "test/fixtures/funny_jokes.yml", "test/fixtures/interests.yml", "test/fixtures/items.yml", "test/fixtures/jobs.yml", "test/fixtures/legacy_things.yml", "test/fixtures/mateys.yml", "test/fixtures/member_types.yml", "test/fixtures/members.yml", "test/fixtures/memberships.yml", "test/fixtures/men.yml", "test/fixtures/minimalistics.yml", "test/fixtures/mixed_case_monkeys.yml", "test/fixtures/mixins.yml", "test/fixtures/movies.yml", "test/fixtures/naked", "test/fixtures/naked/csv", "test/fixtures/naked/csv/accounts.csv", "test/fixtures/naked/yml", "test/fixtures/naked/yml/accounts.yml", "test/fixtures/naked/yml/companies.yml", "test/fixtures/naked/yml/courses.yml", "test/fixtures/organizations.yml", "test/fixtures/owners.yml", "test/fixtures/parrots.yml", "test/fixtures/parrots_pirates.yml", "test/fixtures/people.yml", "test/fixtures/pets.yml", "test/fixtures/pirates.yml", "test/fixtures/posts.yml", "test/fixtures/price_estimates.yml", "test/fixtures/projects.yml", "test/fixtures/readers.yml", "test/fixtures/references.yml", "test/fixtures/reserved_words", "test/fixtures/reserved_words/distinct.yml", "test/fixtures/reserved_words/distincts_selects.yml", "test/fixtures/reserved_words/group.yml", "test/fixtures/reserved_words/select.yml", "test/fixtures/reserved_words/values.yml", "test/fixtures/ships.yml", "test/fixtures/sponsors.yml", "test/fixtures/subscribers.yml", "test/fixtures/subscriptions.yml", "test/fixtures/taggings.yml", "test/fixtures/tags.yml", "test/fixtures/tasks.yml", "test/fixtures/topics.yml", "test/fixtures/toys.yml", "test/fixtures/treasures.yml", "test/fixtures/vertices.yml", "test/fixtures/warehouse-things.yml", "test/fixtures/zines.yml", "test/migrations", "test/migrations/broken", "test/migrations/broken/100_migration_that_raises_exception.rb", "test/migrations/decimal", "test/migrations/decimal/1_give_me_big_numbers.rb", "test/migrations/duplicate", "test/migrations/duplicate/1_people_have_last_names.rb", "test/migrations/duplicate/2_we_need_reminders.rb", "test/migrations/duplicate/3_foo.rb", "test/migrations/duplicate/3_innocent_jointable.rb", "test/migrations/duplicate_names", "test/migrations/duplicate_names/20080507052938_chunky.rb", "test/migrations/duplicate_names/20080507053028_chunky.rb", "test/migrations/interleaved", "test/migrations/interleaved/pass_1", "test/migrations/interleaved/pass_1/3_innocent_jointable.rb", "test/migrations/interleaved/pass_2", "test/migrations/interleaved/pass_2/1_people_have_last_names.rb", "test/migrations/interleaved/pass_2/3_innocent_jointable.rb", "test/migrations/interleaved/pass_3", "test/migrations/interleaved/pass_3/1_people_have_last_names.rb", "test/migrations/interleaved/pass_3/2_i_raise_on_down.rb", "test/migrations/interleaved/pass_3/3_innocent_jointable.rb", "test/migrations/missing", "test/migrations/missing/1000_people_have_middle_names.rb", "test/migrations/missing/1_people_have_last_names.rb", "test/migrations/missing/3_we_need_reminders.rb", "test/migrations/missing/4_innocent_jointable.rb", "test/migrations/valid", "test/migrations/valid/1_people_have_last_names.rb", "test/migrations/valid/2_we_need_reminders.rb", "test/migrations/valid/3_innocent_jointable.rb", "test/models", "test/models/author.rb", "test/models/auto_id.rb", "test/models/binary.rb", "test/models/bird.rb", "test/models/book.rb", "test/models/categorization.rb", "test/models/category.rb", "test/models/citation.rb", "test/models/club.rb", "test/models/column_name.rb", "test/models/comment.rb", "test/models/company.rb", "test/models/company_in_module.rb", "test/models/computer.rb", "test/models/contact.rb", "test/models/contract.rb", "test/models/course.rb", "test/models/customer.rb", "test/models/default.rb", "test/models/developer.rb", "test/models/edge.rb", "test/models/entrant.rb", "test/models/essay.rb", "test/models/event.rb", "test/models/face.rb", "test/models/guid.rb", "test/models/interest.rb", "test/models/item.rb", "test/models/job.rb", "test/models/joke.rb", "test/models/keyboard.rb", "test/models/legacy_thing.rb", "test/models/man.rb", "test/models/matey.rb", "test/models/member.rb", "test/models/member_detail.rb", "test/models/member_type.rb", "test/models/membership.rb", "test/models/minimalistic.rb", "test/models/mixed_case_monkey.rb", "test/models/movie.rb", "test/models/order.rb", "test/models/organization.rb", "test/models/owner.rb", "test/models/parrot.rb", "test/models/person.rb", "test/models/pet.rb", "test/models/pirate.rb", "test/models/post.rb", "test/models/price_estimate.rb", "test/models/project.rb", "test/models/reader.rb", "test/models/reference.rb", "test/models/reply.rb", "test/models/ship.rb", "test/models/ship_part.rb", "test/models/sponsor.rb", "test/models/subject.rb", "test/models/subscriber.rb", "test/models/subscription.rb", "test/models/tag.rb", "test/models/tagging.rb", "test/models/task.rb", "test/models/topic.rb", "test/models/toy.rb", "test/models/traffic_light.rb", "test/models/treasure.rb", "test/models/vertex.rb", "test/models/warehouse_thing.rb", "test/models/zine.rb", "test/schema", "test/schema/mysql_specific_schema.rb", "test/schema/oracle_specific_schema.rb", "test/schema/postgresql_specific_schema.rb", "test/schema/schema.rb", "test/schema/sqlite_specific_schema.rb", "examples/associations.png", "examples/performance.rb", "examples/simple.rb"]
+ s.homepage = %q{http://www.rubyonrails.org}
+ s.rdoc_options = ["--main", "README"]
+ s.require_paths = ["lib"]
+ s.rubyforge_project = %q{activerecord}
+ s.rubygems_version = %q{1.3.5}
+ s.summary = %q{Implements the ActiveRecord pattern for ORM.}
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ s.add_runtime_dependency(%q<activesupport>, ["= 3.0.pre"])
+ s.add_runtime_dependency(%q<activemodel>, ["= 3.0.pre"])
+ else
+ s.add_dependency(%q<activesupport>, ["= 3.0.pre"])
+ s.add_dependency(%q<activemodel>, ["= 3.0.pre"])
+ end
+ else
+ s.add_dependency(%q<activesupport>, ["= 3.0.pre"])
+ s.add_dependency(%q<activemodel>, ["= 3.0.pre"])
+ end
+end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index f494e38e2f..266a52d612 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -280,9 +280,10 @@ module ActiveRecord
# You can manipulate objects and associations before they are saved to the database, but there is some special behavior you should be
# aware of, mostly involving the saving of associated objects.
#
- # Unless you enable the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
- # <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association,
- # in which case the members are always saved.
+ # Unless you set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
+ # <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it
+ # to +true+ will _always_ save the members, whereas setting it to +false+ will
+ # _never_ save the members.
#
# === One-to-one associations
#
@@ -1491,24 +1492,43 @@ module ActiveRecord
end
before_destroy method_name
when :delete_all
- module_eval %Q{
- before_destroy do |record| # before_destroy do |record|
- delete_all_has_many_dependencies(record, # delete_all_has_many_dependencies(record,
- "#{reflection.name}", # "posts",
- #{reflection.class_name}, # Post,
- %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...)
- end # end
- }
+ # before_destroy do |record|
+ # self.class.send(:delete_all_has_many_dependencies,
+ # record,
+ # "posts",
+ # Post,
+ # %@...@) # this is a string literal like %(...)
+ # end
+ # end
+ module_eval <<-CALLBACK
+ before_destroy do |record|
+ self.class.send(:delete_all_has_many_dependencies,
+ record,
+ "#{reflection.name}",
+ #{reflection.class_name},
+ %@#{dependent_conditions}@)
+ end
+ CALLBACK
when :nullify
- module_eval %Q{
- before_destroy do |record| # before_destroy do |record|
- nullify_has_many_dependencies(record, # nullify_has_many_dependencies(record,
- "#{reflection.name}", # "posts",
- #{reflection.class_name}, # Post,
- "#{reflection.primary_key_name}", # "user_id",
- %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...)
- end # end
- }
+ # before_destroy do |record|
+ # self.class.send(:nullify_has_many_dependencies,
+ # record,
+ # "posts",
+ # Post,
+ # "user_id",
+ # %@...@) # this is a string literal like %(...)
+ # end
+ # end
+ module_eval <<-CALLBACK
+ before_destroy do |record|
+ self.class.send(:nullify_has_many_dependencies,
+ record,
+ "#{reflection.name}",
+ #{reflection.class_name},
+ "#{reflection.primary_key_name}",
+ %@#{dependent_conditions}@)
+ end
+ CALLBACK
else
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
end
@@ -1656,7 +1676,7 @@ module ActiveRecord
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
-
+
if reflection.association_foreign_key == reflection.primary_key_name
raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection)
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 d91c555dad..417e2fdc0f 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
@@ -24,8 +24,8 @@ module ActiveRecord
def has_primary_key?
return @has_primary_key unless @has_primary_key.nil?
- @has_primary_key = (ActiveRecord::Base.connection.supports_primary_key? &&
- ActiveRecord::Base.connection.primary_key(@reflection.options[:join_table]))
+ @has_primary_key = (@owner.connection.supports_primary_key? &&
+ @owner.connection.primary_key(@reflection.options[:join_table]))
end
protected
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index c1bc8423a9..8f37fcd515 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -158,7 +158,7 @@ module ActiveRecord
def add_autosave_association_callbacks(reflection)
save_method = "autosave_associated_records_for_#{reflection.name}"
validation_method = "validate_associated_records_for_#{reflection.name}"
- validate validation_method
+ force_validation = (reflection.options[:validate] == true || reflection.options[:autosave] == true)
case reflection.macro
when :has_many, :has_and_belongs_to_many
@@ -169,7 +169,10 @@ module ActiveRecord
after_create save_method
after_update save_method
- define_method(validation_method) { validate_collection_association(reflection) }
+ if force_validation || (reflection.macro == :has_many && reflection.options[:validate] != false)
+ define_method(validation_method) { validate_collection_association(reflection) }
+ validate validation_method
+ end
else
case reflection.macro
when :has_one
@@ -179,7 +182,11 @@ module ActiveRecord
define_method(save_method) { save_belongs_to_association(reflection) }
before_save save_method
end
- define_method(validation_method) { validate_single_association(reflection) }
+
+ if force_validation
+ define_method(validation_method) { validate_single_association(reflection) }
+ validate validation_method
+ end
end
end
end
@@ -223,10 +230,8 @@ module ActiveRecord
# Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
# turned on for the association specified by +reflection+.
def validate_single_association(reflection)
- if reflection.options[:validate] == true || reflection.options[:autosave] == true
- if (association = association_instance_get(reflection.name)) && !association.target.nil?
- association_valid?(reflection, association)
- end
+ if (association = association_instance_get(reflection.name)) && !association.target.nil?
+ association_valid?(reflection, association)
end
end
@@ -234,7 +239,7 @@ module ActiveRecord
# <tt>:autosave</tt> is turned on for the association specified by
# +reflection+.
def validate_collection_association(reflection)
- if reflection.options[:validate] != false && association = association_instance_get(reflection.name)
+ if association = association_instance_get(reflection.name)
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
records.each { |record| association_valid?(reflection, record) }
end
@@ -243,15 +248,15 @@ module ActiveRecord
# Returns whether or not the association is valid and applies any errors to
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
- # enabled records if they're marked_for_destruction?.
+ # enabled records if they're marked_for_destruction? or destroyed.
def association_valid?(reflection, association)
+ return true if association.destroyed? || association.marked_for_destruction?
+
unless valid = association.valid?
if reflection.options[:autosave]
- unless association.marked_for_destruction?
- association.errors.each do |attribute, message|
- attribute = "#{reflection.name}_#{attribute}"
- errors[attribute] << message if errors[attribute].empty?
- end
+ association.errors.each do |attribute, message|
+ attribute = "#{reflection.name}_#{attribute}"
+ errors[attribute] << message if errors[attribute].empty?
end
else
errors.add(reflection.name)
@@ -281,9 +286,11 @@ module ActiveRecord
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
records.each do |record|
+ next if record.destroyed?
+
if autosave && record.marked_for_destruction?
association.destroy(record)
- elsif @new_record_before_save || record.new_record?
+ elsif autosave != false && (@new_record_before_save || record.new_record?)
if autosave
association.send(:insert_record, record, false, false)
else
@@ -309,14 +316,17 @@ module ActiveRecord
# This all happens inside a transaction, _if_ the Transactions module is included into
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
def save_has_one_association(reflection)
- if (association = association_instance_get(reflection.name)) && !association.target.nil?
+ if (association = association_instance_get(reflection.name)) && !association.target.nil? && !association.destroyed?
autosave = reflection.options[:autosave]
if autosave && association.marked_for_destruction?
association.destroy
- elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave
- association[reflection.primary_key_name] = id
- association.save(!autosave)
+ else
+ key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
+ if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave)
+ association[reflection.primary_key_name] = key
+ association.save(!autosave)
+ end
end
end
end
@@ -330,12 +340,12 @@ module ActiveRecord
# This all happens inside a transaction, _if_ the Transactions module is included into
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
def save_belongs_to_association(reflection)
- if association = association_instance_get(reflection.name)
+ if (association = association_instance_get(reflection.name)) && !association.destroyed?
autosave = reflection.options[:autosave]
if autosave && association.marked_for_destruction?
association.destroy
- else
+ elsif autosave != false
association.save(!autosave) if association.new_record? || autosave
if association.updated?
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index c5be561ea3..502fe0442e 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1648,46 +1648,31 @@ module ActiveRecord #:nodoc:
# single-table inheritance model that makes it possible to create
# objects of different types from the same table.
def instantiate(record)
- object =
- if subclass_name = record[inheritance_column]
- # No type given.
- if subclass_name.empty?
- allocate
+ object = find_sti_class(record[inheritance_column]).allocate
- else
- # Ignore type if no column is present since it was probably
- # pulled in from a sloppy join.
- unless columns_hash.include?(inheritance_column)
- allocate
+ object.instance_variable_set(:'@attributes', record)
+ object.instance_variable_set(:'@attributes_cache', {})
- else
- begin
- compute_type(subclass_name).allocate
- rescue NameError
- raise SubclassNotFound,
- "The single-table inheritance mechanism failed to locate the subclass: '#{record[inheritance_column]}'. " +
- "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
- "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
- "or overwrite #{self.to_s}.inheritance_column to use another column for that information."
- end
- end
- end
- else
- allocate
- end
+ object.send(:_run_find_callbacks)
+ object.send(:_run_initialize_callbacks)
- object.instance_variable_set("@attributes", record)
- object.instance_variable_set("@attributes_cache", Hash.new)
-
- if object.respond_to_without_attributes?(:after_find)
- object.send(:callback, :after_find)
- end
+ object
+ end
- if object.respond_to_without_attributes?(:after_initialize)
- object.send(:callback, :after_initialize)
+ def find_sti_class(type_name)
+ if type_name.blank? || !columns_hash.include?(inheritance_column)
+ self
+ else
+ begin
+ compute_type(type_name)
+ rescue NameError
+ raise SubclassNotFound,
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
+ "or overwrite #{name}.inheritance_column to use another column for that information."
+ end
end
-
- object
end
# Nest the type name in the same module as this class.
@@ -2438,7 +2423,7 @@ module ActiveRecord #:nodoc:
self.attributes = attributes unless attributes.nil?
self.class.send(:scope, :create).each { |att,value| self.send("#{att}=", value) } if self.class.send(:scoped?, :create)
result = yield self if block_given?
- callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
+ _run_initialize_callbacks
result
end
@@ -2943,7 +2928,9 @@ module ActiveRecord #:nodoc:
end
def log_protected_attribute_removal(*attributes)
- logger.debug "WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}"
+ if logger
+ logger.debug "WARNING: Can't mass-assign these protected attributes: #{attributes.join(', ')}"
+ end
end
# The primary key and inheritance column can never be set by mass-assignment for security reasons.
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index 4a2ec5bf95..40a25811c4 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -10,16 +10,14 @@ module ActiveRecord
# * (-) <tt>save</tt>
# * (-) <tt>valid</tt>
# * (1) <tt>before_validation</tt>
- # * (2) <tt>before_validation_on_create</tt>
# * (-) <tt>validate</tt>
# * (-) <tt>validate_on_create</tt>
- # * (3) <tt>after_validation</tt>
- # * (4) <tt>after_validation_on_create</tt>
- # * (5) <tt>before_save</tt>
- # * (6) <tt>before_create</tt>
+ # * (2) <tt>after_validation</tt>
+ # * (3) <tt>before_save</tt>
+ # * (4) <tt>before_create</tt>
# * (-) <tt>create</tt>
- # * (7) <tt>after_create</tt>
- # * (8) <tt>after_save</tt>
+ # * (5) <tt>after_create</tt>
+ # * (6) <tt>after_save</tt>
#
# That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the
# Active Record lifecycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar, except that each
@@ -212,162 +210,122 @@ module ActiveRecord
# instead of quietly returning +false+.
module Callbacks
extend ActiveSupport::Concern
+ include ActiveSupport::NewCallbacks
- CALLBACKS = %w(
- after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
- after_validation before_validation_on_create after_validation_on_create before_validation_on_update
- after_validation_on_update before_destroy after_destroy
- )
+ CALLBACKS = [
+ :after_initialize, :after_find, :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
+ ]
included do
- extend Observable
-
[:create_or_update, :valid?, :create, :update, :destroy].each do |method|
alias_method_chain method, :callbacks
end
- include ActiveSupport::Callbacks
- define_callbacks *CALLBACKS
+ define_callbacks :initialize, :find, :save, :create, :update, :destroy,
+ :validation, :terminator => "result == false", :scope => [:kind, :name]
end
- # Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
- #def after_find() end
+ module ClassMethods
+ def after_initialize(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ set_callback(:initialize, :after, *(args << options), &block)
+ end
+
+ def after_find(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ set_callback(:find, :after, *(args << options), &block)
+ end
+
+ [:save, :create, :update, :destroy].each do |callback|
+ module_eval <<-CALLBACKS, __FILE__, __LINE__
+ def before_#{callback}(*args, &block)
+ set_callback(:#{callback}, :before, *args, &block)
+ end
- # Is called after the object has been instantiated by a call to <tt>Base.new</tt>.
- #def after_initialize() end
+ def around_#{callback}(*args, &block)
+ set_callback(:#{callback}, :around, *args, &block)
+ end
- # Is called _before_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
- def before_save() end
+ def after_#{callback}(*args, &block)
+ options = args.extract_options!
+ options[:prepend] = true
+ options[:if] = Array(options[:if]) << "!halted && value != false"
+ set_callback(:#{callback}, :after, *(args << options), &block)
+ end
+ CALLBACKS
+ end
+
+ def before_validation(*args, &block)
+ options = args.extract_options!
+ if options[:on]
+ options[:if] = Array(options[:if])
+ options[:if] << "@_on_validate == :#{options[:on]}"
+ end
+ set_callback(:validation, :before, *(args << options), &block)
+ end
+
+ def after_validation(*args, &block)
+ options = args.extract_options!
+ options[:if] = Array(options[:if])
+ options[:if] << "!halted"
+ options[:if] << "@_on_validate == :#{options[:on]}" if options[:on]
+ options[:prepend] = true
+ set_callback(:validation, :after, *(args << options), &block)
+ end
+
+ def method_added(meth)
+ super
+ if CALLBACKS.include?(meth.to_sym)
+ ActiveSupport::Deprecation.warn("Base##{meth} has been deprecated, please use Base.#{meth} :method instead", caller[0,1])
+ send(meth.to_sym, meth.to_sym)
+ end
+ end
+ end
- # Is called _after_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
- # Note that this callback is still wrapped in the transaction around +save+. For example, if you
- # invoke an external indexer at this point it won't see the changes in the database.
- #
- # class Contact < ActiveRecord::Base
- # after_save { logger.info( 'New contact saved!' ) }
- # end
- def after_save() end
def create_or_update_with_callbacks #:nodoc:
- return false if callback(:before_save) == false
- if result = create_or_update_without_callbacks
- callback(:after_save)
+ _run_save_callbacks do
+ create_or_update_without_callbacks
end
- result
end
private :create_or_update_with_callbacks
- # Is called _before_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
- def before_create() end
-
- # Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
- # Note that this callback is still wrapped in the transaction around +save+. For example, if you
- # invoke an external indexer at this point it won't see the changes in the database.
- #
- # class Contact < ActiveRecord::Base
- # after_create { |record| logger.info( "Contact #{record.id} was created." ) }
- # end
- def after_create() end
def create_with_callbacks #:nodoc:
- return false if callback(:before_create) == false
- result = create_without_callbacks
- callback(:after_create)
- result
+ _run_create_callbacks do
+ create_without_callbacks
+ end
end
private :create_with_callbacks
- # Is called _before_ <tt>Base.save</tt> on existing objects that have a record.
- #
- # class Contact < ActiveRecord::Base
- # before_update { |record| logger.info( "Contact #{record.id} is about to be updated." ) }
- # end
- def before_update() end
-
- # Is called _after_ <tt>Base.save</tt> on existing objects that have a record.
- # Note that this callback is still wrapped in the transaction around +save+. For example, if you
- # invoke an external indexer at this point it won't see the changes in the database.
- #
- # class Contact < ActiveRecord::Base
- # after_update { |record| logger.info( "Contact #{record.id} was updated." ) }
- # end
- def after_update() end
-
def update_with_callbacks(*args) #:nodoc:
- return false if callback(:before_update) == false
- result = update_without_callbacks(*args)
- callback(:after_update)
- result
+ _run_update_callbacks do
+ update_without_callbacks(*args)
+ end
end
private :update_with_callbacks
- # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
- def before_validation() end
-
- # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
- def after_validation() end
-
- # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
- # that haven't been saved yet (no record exists).
- def before_validation_on_create() end
-
- # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
- # that haven't been saved yet (no record exists).
- def after_validation_on_create() end
-
- # Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
- # existing objects that have a record.
- def before_validation_on_update() end
-
- # Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
- # existing objects that have a record.
- def after_validation_on_update() end
-
def valid_with_callbacks? #:nodoc:
- return false if callback(:before_validation) == false
- if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
- return false if false == result
-
- result = valid_without_callbacks?
-
- callback(:after_validation)
- if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
-
- return result
+ @_on_validate = new_record? ? :create : :update
+ _run_validation_callbacks do
+ valid_without_callbacks?
+ end
end
- # Is called _before_ <tt>Base.destroy</tt>.
- #
- # Note: If you need to _destroy_ or _nullify_ associated records first,
- # use the <tt>:dependent</tt> option on your associations.
- #
- # class Contact < ActiveRecord::Base
- # after_destroy { |record| logger.info( "Contact #{record.id} is about to be destroyed." ) }
- # end
- def before_destroy() end
-
- # Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen).
- #
- # class Contact < ActiveRecord::Base
- # after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) }
- # end
- def after_destroy() end
def destroy_with_callbacks #:nodoc:
- return false if callback(:before_destroy) == false
- result = destroy_without_callbacks
- callback(:after_destroy)
- result
+ _run_destroy_callbacks do
+ destroy_without_callbacks
+ end
end
- private
- def callback(method)
- result = run_callbacks(method) { |result, object| false == result }
-
- if result != false && respond_to_without_attributes?(method)
- result = send(method)
- end
-
- notify_observers(method)
-
- return result
+ def deprecated_callback_method(symbol) #:nodoc:
+ if respond_to?(symbol)
+ ActiveSupport::Deprecation.warn("Base##{symbol} has been deprecated, please use Base.#{symbol} :method instead")
+ send(symbol)
end
+ end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index fab70f34b9..78c7a4b697 100755
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -201,16 +201,12 @@ module ActiveRecord
protected
def log(sql, name)
- if block_given?
- result = nil
- ms = Benchmark.ms { result = yield }
- @runtime += ms
- log_info(sql, name, ms)
- result
- else
- log_info(sql, name, 0)
- nil
+ event = ActiveSupport::Orchestra.instrument(:sql, :sql => sql, :name => name) do
+ yield if block_given?
end
+ @runtime += event.duration
+ log_info(sql, name, event.duration)
+ event.result
rescue Exception => e
# Log message and raise exception.
# Set last_verification to 0, so that connection gets verified
@@ -221,10 +217,10 @@ module ActiveRecord
raise translate_exception(e, message)
end
- def translate_exception(e, message)
- # override in derived class
- ActiveRecord::StatementInvalid.new(message)
- end
+ def translate_exception(e, message)
+ # override in derived class
+ ActiveRecord::StatementInvalid.new(message)
+ end
def format_log_entry(message, dump = nil)
if ActiveRecord::Base.colorize_logging
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 4edb64c2c0..1072eb7ac1 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -8,7 +8,8 @@ module MysqlCompat #:nodoc:
raise 'Mysql not loaded' unless defined?(::Mysql)
target = defined?(Mysql::Result) ? Mysql::Result : MysqlRes
- return if target.instance_methods.include?('all_hashes')
+ return if target.instance_methods.include?('all_hashes') ||
+ target.instance_methods.include?(:all_hashes)
# Ruby driver has a version string and returns null values in each_hash
# C driver >= 2.7 returns null values in each_hash
@@ -64,12 +65,15 @@ module ActiveRecord
raise
end
end
+
MysqlCompat.define_all_hashes_method!
mysql = Mysql.init
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]
- ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config)
+ default_flags = Mysql.const_defined?(:CLIENT_MULTI_RESULTS) ? Mysql::CLIENT_MULTI_RESULTS : 0
+ options = [host, username, password, database, port, socket, default_flags]
+ ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config)
end
end
diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb
index 320659596f..fcc9ebb4af 100644
--- a/activerecord/lib/active_record/locking/pessimistic.rb
+++ b/activerecord/lib/active_record/locking/pessimistic.rb
@@ -1,25 +1,3 @@
-# Copyright (c) 2006 Shugo Maeda <shugo@ruby-lang.org>
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject
-# to the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
-# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-
module ActiveRecord
module Locking
# Locking::Pessimistic provides support for row-level locking using
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index de48b10c1c..cd3b182b09 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -66,10 +66,10 @@ module ActiveRecord
# accepts_nested_attributes_for :avatar, :allow_destroy => true
# end
#
- # Now, when you add the <tt>_delete</tt> key to the attributes hash, with a
+ # Now, when you add the <tt>_destroy</tt> key to the attributes hash, with a
# value that evaluates to +true+, you will destroy the associated model:
#
- # member.avatar_attributes = { :id => '2', :_delete => '1' }
+ # member.avatar_attributes = { :id => '2', :_destroy => '1' }
# member.avatar.marked_for_destruction? # => true
# member.save
# member.reload.avatar #=> nil
@@ -89,14 +89,14 @@ module ActiveRecord
# the attribute hash.
#
# For each hash that does _not_ have an <tt>id</tt> key a new record will
- # be instantiated, unless the hash also contains a <tt>_delete</tt> key
+ # be instantiated, unless the hash also contains a <tt>_destroy</tt> key
# that evaluates to +true+.
#
# params = { :member => {
# :name => 'joe', :posts_attributes => [
# { :title => 'Kari, the awesome Ruby documentation browser!' },
# { :title => 'The egalitarian assumption of the modern citizen' },
- # { :title => '', :_delete => '1' } # this will be ignored
+ # { :title => '', :_destroy => '1' } # this will be ignored
# ]
# }}
#
@@ -144,7 +144,7 @@ module ActiveRecord
# By default the associated records are protected from being destroyed. If
# you want to destroy any of the associated records through the attributes
# hash, you have to enable it first using the <tt>:allow_destroy</tt>
- # option. This will allow you to also use the <tt>_delete</tt> key to
+ # option. This will allow you to also use the <tt>_destroy</tt> key to
# destroy existing records:
#
# class Member < ActiveRecord::Base
@@ -153,7 +153,7 @@ module ActiveRecord
# end
#
# params = { :member => {
- # :posts_attributes => [{ :id => '2', :_delete => '1' }]
+ # :posts_attributes => [{ :id => '2', :_destroy => '1' }]
# }}
#
# member.attributes = params['member']
@@ -176,14 +176,14 @@ module ActiveRecord
# Supported options:
# [:allow_destroy]
# If true, destroys any members from the attributes hash with a
- # <tt>_delete</tt> key and a value that evaluates to +true+
+ # <tt>_destroy</tt> key and a value that evaluates to +true+
# (eg. 1, '1', true, or 'true'). This option is off by default.
# [:reject_if]
# Allows you to specify a Proc that checks whether a record should be
# built for a certain attribute hash. The hash is passed to the Proc
# and the Proc should return either +true+ or +false+. When no Proc
# is specified a record will be built for all attribute hashes that
- # do not have a <tt>_delete</tt> that evaluates to true.
+ # do not have a <tt>_destroy</tt> value that evaluates to true.
# Passing <tt>:all_blank</tt> instead of a Proc will create a proc
# that will reject a record where all the attributes are blank.
#
@@ -236,15 +236,25 @@ module ActiveRecord
# destruction of this association.
#
# See ActionView::Helpers::FormHelper::fields_for for more info.
- def _delete
+ def _destroy
marked_for_destruction?
end
+ # Deal with deprecated _delete.
+ #
+ def _delete #:nodoc:
+ ActiveSupport::Deprecation.warn "_delete is deprecated in nested attributes. Use _destroy instead."
+ _destroy
+ end
+
private
# Attribute hash keys that should not be assigned as normal attributes.
# These hash keys are nested attributes implementation details.
- UNASSIGNABLE_KEYS = %w{ id _delete }
+ #
+ # TODO Remove _delete from UNASSIGNABLE_KEYS when deprecation warning are
+ # removed.
+ UNASSIGNABLE_KEYS = %w( id _destroy _delete )
# Assigns the given attributes to the association.
#
@@ -253,14 +263,19 @@ module ActiveRecord
# record will be built.
#
# If the given attributes include a matching <tt>:id</tt> attribute _and_ a
- # <tt>:_delete</tt> key set to a truthy value, then the existing record
+ # <tt>:_destroy</tt> key set to a truthy value, then the existing record
# will be marked for destruction.
def assign_nested_attributes_for_one_to_one_association(association_name, attributes, allow_destroy)
attributes = attributes.stringify_keys
if attributes['id'].blank?
unless reject_new_record?(association_name, attributes)
- send("build_#{association_name}", attributes.except(*UNASSIGNABLE_KEYS))
+ method = "build_#{association_name}"
+ if respond_to?(method)
+ send(method, attributes.except(*UNASSIGNABLE_KEYS))
+ else
+ raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
+ end
end
elsif (existing_record = send(association_name)) && existing_record.id.to_s == attributes['id'].to_s
assign_to_or_mark_for_destruction(existing_record, attributes, allow_destroy)
@@ -272,7 +287,7 @@ module ActiveRecord
# Hashes with an <tt>:id</tt> value matching an existing associated record
# will update that record. Hashes without an <tt>:id</tt> value will build
# a new record for the association. Hashes with a matching <tt>:id</tt>
- # value and a <tt>:_delete</tt> key set to a truthy value will mark the
+ # value and a <tt>:_destroy</tt> key set to a truthy value will mark the
# matched record for destruction.
#
# For example:
@@ -280,7 +295,7 @@ module ActiveRecord
# assign_nested_attributes_for_collection_association(:people, {
# '1' => { :id => '1', :name => 'Peter' },
# '2' => { :name => 'John' },
- # '3' => { :id => '2', :_delete => true }
+ # '3' => { :id => '2', :_destroy => true }
# })
#
# Will update the name of the Person with ID 1, build a new associated
@@ -292,7 +307,7 @@ module ActiveRecord
# assign_nested_attributes_for_collection_association(:people, [
# { :id => '1', :name => 'Peter' },
# { :name => 'John' },
- # { :id => '2', :_delete => true }
+ # { :id => '2', :_destroy => true }
# ])
def assign_nested_attributes_for_collection_association(association_name, attributes_collection, allow_destroy)
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
@@ -317,25 +332,26 @@ module ActiveRecord
end
# Updates a record with the +attributes+ or marks it for destruction if
- # +allow_destroy+ is +true+ and has_delete_flag? returns +true+.
+ # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
- if has_delete_flag?(attributes) && allow_destroy
+ if has_destroy_flag?(attributes) && allow_destroy
record.mark_for_destruction
else
record.attributes = attributes.except(*UNASSIGNABLE_KEYS)
end
end
- # Determines if a hash contains a truthy _delete key.
- def has_delete_flag?(hash)
- ConnectionAdapters::Column.value_to_boolean hash['_delete']
+ # Determines if a hash contains a truthy _destroy key.
+ def has_destroy_flag?(hash)
+ ConnectionAdapters::Column.value_to_boolean(hash['_destroy']) ||
+ ConnectionAdapters::Column.value_to_boolean(hash['_delete']) # TODO Remove after deprecation.
end
# Determines if a new record should be build by checking for
- # has_delete_flag? or if a <tt>:reject_if</tt> proc exists for this
+ # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
# association and evaluates to +true+.
def reject_new_record?(association_name, attributes)
- has_delete_flag?(attributes) ||
+ has_destroy_flag?(attributes) ||
self.class.reject_new_nested_attributes_procs[association_name].try(:call, attributes)
end
end
diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb
index a34ff4a47a..4e05b819b5 100644
--- a/activerecord/lib/active_record/observer.rb
+++ b/activerecord/lib/active_record/observer.rb
@@ -1,6 +1,3 @@
-require 'singleton'
-require 'set'
-
module ActiveRecord
# Observer classes respond to lifecycle callbacks to implement trigger-like
# behavior outside the original class. This is a great way to reduce the
@@ -88,11 +85,17 @@ module ActiveRecord
# singletons and that call instantiates and registers them.
#
class Observer < ActiveModel::Observer
+ extlib_inheritable_accessor(:observed_methods){ [] }
+
def initialize
super
observed_subclasses.each { |klass| add_observer!(klass) }
end
+ def self.method_added(method)
+ observed_methods << method if ActiveRecord::Callbacks::CALLBACKS.include?(method.to_sym)
+ end
+
protected
def observed_subclasses
observed_classes.sum([]) { |klass| klass.send(:subclasses) }
@@ -100,8 +103,15 @@ module ActiveRecord
def add_observer!(klass)
super
- if respond_to?(:after_find) && !klass.method_defined?(:after_find)
- klass.class_eval 'def after_find() end'
+
+ # Check if a notifier callback was already added to the given class. If
+ # it was not, add it.
+ self.observed_methods.each do |method|
+ callback = :"_notify_observers_for_#{method}"
+ if (klass.instance_methods & [callback, callback.to_s]).empty?
+ klass.class_eval "def #{callback}; notify_observers(:#{method}); end"
+ klass.send(method, callback)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 5fc41cf054..ab79b520a2 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -110,8 +110,6 @@ module ActiveRecord
included do
alias_method_chain :save, :validation
alias_method_chain :save!, :validation
-
- define_callbacks :validate_on_create, :validate_on_update
end
module ClassMethods
@@ -127,17 +125,6 @@ module ActiveRecord
object
end
end
-
- def validation_method(on)
- case on
- when :create
- :validate_on_create
- when :update
- :validate_on_update
- else
- :validate
- end
- end
end
module InstanceMethods
@@ -165,27 +152,15 @@ module ActiveRecord
def valid?
errors.clear
- run_callbacks(:validate)
+ @_on_validate = new_record? ? :create : :update
+ _run_validate_callbacks
- if respond_to?(:validate)
- ActiveSupport::Deprecation.warn("Base#validate has been deprecated, please use Base.validate :method instead")
- validate
- end
+ deprecated_callback_method(:validate)
if new_record?
- run_callbacks(:validate_on_create)
-
- if respond_to?(:validate_on_create)
- ActiveSupport::Deprecation.warn("Base#validate_on_create has been deprecated, please use Base.validate_on_create :method instead")
- validate_on_create
- end
+ deprecated_callback_method(:validate_on_create)
else
- run_callbacks(:validate_on_update)
-
- if respond_to?(:validate_on_update)
- ActiveSupport::Deprecation.warn("Base#validate_on_update has been deprecated, please use Base.validate_on_update :method instead")
- validate_on_update
- end
+ deprecated_callback_method(:validate_on_update)
end
errors.empty?
diff --git a/activerecord/lib/activerecord.rb b/activerecord/lib/activerecord.rb
deleted file mode 100644
index cd62b2afdc..0000000000
--- a/activerecord/lib/activerecord.rb
+++ /dev/null
@@ -1 +0,0 @@
-require 'active_record'
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index f7178f2c5e..b193f8d8ba 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -813,7 +813,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = companies(:first_firm)
clients = firm.clients
assert_equal 2, clients.length
- clients.last.instance_eval { def before_destroy() raise "Trigger rollback" end }
+ clients.last.instance_eval { def overwrite_to_raise() raise "Trigger rollback" end }
firm.destroy rescue "do nothing"
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 7140de77ea..cdac86a3b9 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -36,6 +36,15 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal accounts(:rails_core_account), firm.account_using_primary_key
end
+ def test_update_with_foreign_and_primary_keys
+ firm = companies(:first_firm)
+ account = firm.account_using_foreign_and_primary_keys
+ assert_equal Account.find_by_firm_name(firm.name), account
+ firm.save
+ firm.reload
+ assert_equal account, firm.account_using_foreign_and_primary_keys
+ end
+
def test_can_marshal_has_one_association_with_nil_target
firm = Firm.new
assert_nothing_raised do
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 271086af8e..9164701601 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -443,6 +443,70 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
end
end
+class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase
+ def test_autosave_new_record_on_belongs_to_can_be_disabled_per_relationship
+ new_account = Account.new("credit_limit" => 1000)
+ new_firm = Firm.new("name" => "some firm")
+
+ assert new_firm.new_record?
+ new_account.firm = new_firm
+ new_account.save!
+
+ assert !new_firm.new_record?
+
+ new_account = Account.new("credit_limit" => 1000)
+ new_autosaved_firm = Firm.new("name" => "some firm")
+
+ assert new_autosaved_firm.new_record?
+ new_account.unautosaved_firm = new_autosaved_firm
+ new_account.save!
+
+ assert new_autosaved_firm.new_record?
+ end
+
+ def test_autosave_new_record_on_has_one_can_be_disabled_per_relationship
+ firm = Firm.new("name" => "some firm")
+ account = Account.new("credit_limit" => 1000)
+
+ assert account.new_record?
+ firm.account = account
+ firm.save!
+
+ assert !account.new_record?
+
+ firm = Firm.new("name" => "some firm")
+ account = Account.new("credit_limit" => 1000)
+
+ firm.unautosaved_account = account
+
+ assert account.new_record?
+ firm.unautosaved_account = account
+ firm.save!
+
+ assert account.new_record?
+ end
+
+ def test_autosave_new_record_on_has_many_can_be_disabled_per_relationship
+ firm = Firm.new("name" => "some firm")
+ account = Account.new("credit_limit" => 1000)
+
+ assert account.new_record?
+ firm.accounts << account
+
+ firm.save!
+ assert !account.new_record?
+
+ firm = Firm.new("name" => "some firm")
+ account = Account.new("credit_limit" => 1000)
+
+ assert account.new_record?
+ firm.unautosaved_accounts << account
+
+ firm.save!
+ assert account.new_record?
+ end
+end
+
class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false
@@ -480,9 +544,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
assert !@pirate.valid?
@pirate.ship.mark_for_destruction
+ @pirate.ship.expects(:valid?).never
assert_difference('Ship.count', -1) { @pirate.save! }
end
+ def test_a_child_marked_for_destruction_should_not_be_destroyed_twice
+ @pirate.ship.mark_for_destruction
+ assert @pirate.save
+ @pirate.ship.expects(:destroy).never
+ assert @pirate.save
+ end
+
def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_child
# Stub the save method of the @pirate.ship instance to destroy and then raise an exception
class << @pirate.ship
@@ -517,9 +589,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
assert !@ship.valid?
@ship.pirate.mark_for_destruction
+ @ship.pirate.expects(:valid?).never
assert_difference('Pirate.count', -1) { @ship.save! }
end
+ def test_a_parent_marked_for_destruction_should_not_be_destroyed_twice
+ @ship.pirate.mark_for_destruction
+ assert @ship.save
+ @ship.pirate.expects(:destroy).never
+ assert @ship.save
+ end
+
def test_should_rollback_destructions_if_an_exception_occurred_while_saving_a_parent
# Stub the save method of the @ship.pirate instance to destroy and then raise an exception
class << @ship.pirate
@@ -560,9 +640,33 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
children.each { |child| child.name = '' }
assert !@pirate.valid?
- children.each { |child| child.mark_for_destruction }
+ children.each do |child|
+ child.mark_for_destruction
+ child.expects(:valid?).never
+ end
assert_difference("#{association_name.classify}.count", -2) { @pirate.save! }
end
+
+ define_method("test_should_skip_validation_on_the_#{association_name}_association_if_destroyed") do
+ @pirate.send(association_name).create!(:name => "#{association_name}_1")
+ children = @pirate.send(association_name)
+
+ children.each { |child| child.name = '' }
+ assert !@pirate.valid?
+
+ children.each { |child| child.destroy }
+ assert @pirate.valid?
+ end
+
+ define_method("test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_#{association_name}") do
+ @pirate.send(association_name).create!(:name => "#{association_name}_1")
+ children = @pirate.send(association_name)
+
+ children.each { |child| child.mark_for_destruction }
+ assert @pirate.save
+ children.each { |child| child.expects(:destroy).never }
+ assert @pirate.save
+ end
define_method("test_should_rollback_destructions_if_an_exception_occurred_while_saving_#{association_name}") do
2.times { |i| @pirate.send(association_name).create!(:name => "#{association_name}_#{i}") }
@@ -952,3 +1056,118 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T
include AutosaveAssociationOnACollectionAssociationTests
end
+
+class TestAutosaveAssociationValidationsOnAHasManyAssocication < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ def setup
+ @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ @pirate.birds.create(:name => 'cookoo')
+ end
+
+ test "should automatically validate associations" do
+ assert @pirate.valid?
+ @pirate.birds.each { |bird| bird.name = '' }
+
+ assert !@pirate.valid?
+ end
+end
+
+class TestAutosaveAssociationValidationsOnAHasOneAssocication < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ def setup
+ @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ @pirate.create_ship(:name => 'titanic')
+ end
+
+ test "should automatically validate associations with :validate => true" do
+ assert @pirate.valid?
+ @pirate.ship.name = ''
+ assert !@pirate.valid?
+ end
+
+ test "should not automatically validate associations without :validate => true" do
+ assert @pirate.valid?
+ @pirate.non_validated_ship.name = ''
+ assert @pirate.valid?
+ end
+end
+
+class TestAutosaveAssociationValidationsOnABelongsToAssocication < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ def setup
+ @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ end
+
+ test "should automatically validate associations with :validate => true" do
+ assert @pirate.valid?
+ @pirate.parrot = Parrot.new(:name => '')
+ assert !@pirate.valid?
+ end
+
+ test "should not automatically validate associations without :validate => true" do
+ assert @pirate.valid?
+ @pirate.non_validated_parrot = Parrot.new(:name => '')
+ assert @pirate.valid?
+ end
+end
+
+class TestAutosaveAssociationValidationsOnAHABTMAssocication < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ def setup
+ @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ end
+
+ test "should automatically validate associations with :validate => true" do
+ assert @pirate.valid?
+ @pirate.parrots = [ Parrot.new(:name => 'popuga') ]
+ @pirate.parrots.each { |parrot| parrot.name = '' }
+ assert !@pirate.valid?
+ end
+
+ test "should not automatically validate associations without :validate => true" do
+ assert @pirate.valid?
+ @pirate.non_validated_parrots = [ Parrot.new(:name => 'popuga') ]
+ @pirate.non_validated_parrots.each { |parrot| parrot.name = '' }
+ assert @pirate.valid?
+ end
+end
+
+class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ def setup
+ @pirate = Pirate.new
+ end
+
+ test "should generate validation methods for has_many associations" do
+ assert @pirate.respond_to?(:validate_associated_records_for_birds)
+ end
+
+ test "should generate validation methods for has_one associations with :validate => true" do
+ assert @pirate.respond_to?(:validate_associated_records_for_ship)
+ end
+
+ test "should not generate validation methods for has_one associations without :validate => true" do
+ assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_ship)
+ end
+
+ test "should generate validation methods for belongs_to associations with :validate => true" do
+ assert @pirate.respond_to?(:validate_associated_records_for_parrot)
+ end
+
+ test "should not generate validation methods for belongs_to associations without :validate => true" do
+ assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrot)
+ end
+
+ test "should generate validation methods for HABTM associations with :validate => true" do
+ assert @pirate.respond_to?(:validate_associated_records_for_parrots)
+ end
+
+ test "should not generate validation methods for HABTM associations without :validate => true" do
+ assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrots)
+ end
+end
diff --git a/activerecord/test/cases/callbacks_observers_test.rb b/activerecord/test/cases/callbacks_observers_test.rb
index 87de524923..52ce384844 100644
--- a/activerecord/test/cases/callbacks_observers_test.rb
+++ b/activerecord/test/cases/callbacks_observers_test.rb
@@ -5,7 +5,7 @@ class Comment < ActiveRecord::Base
before_validation :record_callers
- def after_validation
+ after_validation do
record_callers
end
@@ -32,7 +32,6 @@ class CallbacksObserversTest < ActiveRecord::TestCase
CommentObserver.instance.callers = callers
comment.valid?
-
assert_equal [Comment, Comment, CommentObserver], callers, "model callbacks did not fire before observers were notified"
end
end
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index 95fddaeef6..5a084a611e 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -13,8 +13,8 @@ class CallbackDeveloper < ActiveRecord::Base
end
def define_callback_method(callback_method)
- define_method("#{callback_method}_method") do |model|
- model.history << [callback_method, :method]
+ define_method(callback_method) do
+ self.history << [callback_method, :method]
end
end
@@ -27,26 +27,20 @@ class CallbackDeveloper < ActiveRecord::Base
end
end
- ActiveRecord::Callbacks::CALLBACKS.each do |callback_method|
- callback_method_sym = callback_method.to_sym
- define_callback_method(callback_method_sym)
- send(callback_method, callback_method_sym)
- send(callback_method, callback_string(callback_method_sym))
- send(callback_method, callback_proc(callback_method_sym))
- send(callback_method, callback_object(callback_method_sym))
- send(callback_method) { |model| model.history << [callback_method_sym, :block] }
+ ActiveSupport::Deprecation.silence do
+ ActiveRecord::Callbacks::CALLBACKS.each do |callback_method|
+ next if callback_method.to_s =~ /^around_/
+ define_callback_method(callback_method)
+ send(callback_method, callback_string(callback_method))
+ send(callback_method, callback_proc(callback_method))
+ send(callback_method, callback_object(callback_method))
+ send(callback_method) { |model| model.history << [callback_method, :block] }
+ end
end
def history
@history ||= []
end
-
- # after_initialize and after_find are invoked only if instance methods have been defined.
- def after_initialize
- end
-
- def after_find
- end
end
class ParentDeveloper < ActiveRecord::Base
@@ -108,12 +102,12 @@ class ImmutableMethodDeveloper < ActiveRecord::Base
@cancelled == true
end
- def before_save
+ before_save do
@cancelled = true
false
end
- def before_destroy
+ before_destroy do
@cancelled = true
false
end
@@ -125,15 +119,15 @@ class CallbackCancellationDeveloper < ActiveRecord::Base
attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called
attr_accessor :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy
- def before_save; !@cancel_before_save; end
- def before_create; !@cancel_before_create; end
- def before_update; !@cancel_before_update; end
- def before_destroy; !@cancel_before_destroy; end
+ before_save { !@cancel_before_save }
+ before_create { !@cancel_before_create }
+ before_update { !@cancel_before_update }
+ before_destroy { !@cancel_before_destroy }
- def after_save; @after_save_called = true; end
- def after_update; @after_update_called = true; end
- def after_create; @after_create_called = true; end
- def after_destroy; @after_destroy_called = true; end
+ after_save { @after_save_called = true }
+ after_update { @after_update_called = true }
+ after_create { @after_create_called = true }
+ after_destroy { @after_destroy_called = true }
end
class CallbacksTest < ActiveRecord::TestCase
@@ -142,6 +136,7 @@ class CallbacksTest < ActiveRecord::TestCase
def test_initialize
david = CallbackDeveloper.new
assert_equal [
+ [ :after_initialize, :method ],
[ :after_initialize, :string ],
[ :after_initialize, :proc ],
[ :after_initialize, :object ],
@@ -152,10 +147,12 @@ class CallbacksTest < ActiveRecord::TestCase
def test_find
david = CallbackDeveloper.find(1)
assert_equal [
+ [ :after_find, :method ],
[ :after_find, :string ],
[ :after_find, :proc ],
[ :after_find, :object ],
[ :after_find, :block ],
+ [ :after_initialize, :method ],
[ :after_initialize, :string ],
[ :after_initialize, :proc ],
[ :after_initialize, :object ],
@@ -167,26 +164,21 @@ class CallbacksTest < ActiveRecord::TestCase
david = CallbackDeveloper.new
david.valid?
assert_equal [
+ [ :after_initialize, :method ],
[ :after_initialize, :string ],
[ :after_initialize, :proc ],
[ :after_initialize, :object ],
[ :after_initialize, :block ],
+ [ :before_validation, :method ],
[ :before_validation, :string ],
[ :before_validation, :proc ],
[ :before_validation, :object ],
[ :before_validation, :block ],
- [ :before_validation_on_create, :string ],
- [ :before_validation_on_create, :proc ],
- [ :before_validation_on_create, :object ],
- [ :before_validation_on_create, :block ],
+ [ :after_validation, :method ],
[ :after_validation, :string ],
[ :after_validation, :proc ],
[ :after_validation, :object ],
[ :after_validation, :block ],
- [ :after_validation_on_create, :string ],
- [ :after_validation_on_create, :proc ],
- [ :after_validation_on_create, :object ],
- [ :after_validation_on_create, :block ]
], david.history
end
@@ -194,68 +186,63 @@ class CallbacksTest < ActiveRecord::TestCase
david = CallbackDeveloper.find(1)
david.valid?
assert_equal [
+ [ :after_find, :method ],
[ :after_find, :string ],
[ :after_find, :proc ],
[ :after_find, :object ],
[ :after_find, :block ],
+ [ :after_initialize, :method ],
[ :after_initialize, :string ],
[ :after_initialize, :proc ],
[ :after_initialize, :object ],
[ :after_initialize, :block ],
+ [ :before_validation, :method ],
[ :before_validation, :string ],
[ :before_validation, :proc ],
[ :before_validation, :object ],
[ :before_validation, :block ],
- [ :before_validation_on_update, :string ],
- [ :before_validation_on_update, :proc ],
- [ :before_validation_on_update, :object ],
- [ :before_validation_on_update, :block ],
+ [ :after_validation, :method ],
[ :after_validation, :string ],
[ :after_validation, :proc ],
[ :after_validation, :object ],
[ :after_validation, :block ],
- [ :after_validation_on_update, :string ],
- [ :after_validation_on_update, :proc ],
- [ :after_validation_on_update, :object ],
- [ :after_validation_on_update, :block ]
], david.history
end
def test_create
david = CallbackDeveloper.create('name' => 'David', 'salary' => 1000000)
assert_equal [
+ [ :after_initialize, :method ],
[ :after_initialize, :string ],
[ :after_initialize, :proc ],
[ :after_initialize, :object ],
[ :after_initialize, :block ],
+ [ :before_validation, :method ],
[ :before_validation, :string ],
[ :before_validation, :proc ],
[ :before_validation, :object ],
[ :before_validation, :block ],
- [ :before_validation_on_create, :string ],
- [ :before_validation_on_create, :proc ],
- [ :before_validation_on_create, :object ],
- [ :before_validation_on_create, :block ],
+ [ :after_validation, :method ],
[ :after_validation, :string ],
[ :after_validation, :proc ],
[ :after_validation, :object ],
[ :after_validation, :block ],
- [ :after_validation_on_create, :string ],
- [ :after_validation_on_create, :proc ],
- [ :after_validation_on_create, :object ],
- [ :after_validation_on_create, :block ],
+ [ :before_save, :method ],
[ :before_save, :string ],
[ :before_save, :proc ],
[ :before_save, :object ],
[ :before_save, :block ],
+ [ :before_create, :method ],
[ :before_create, :string ],
[ :before_create, :proc ],
[ :before_create, :object ],
[ :before_create, :block ],
+ [ :after_create, :method ],
[ :after_create, :string ],
[ :after_create, :proc ],
[ :after_create, :object ],
[ :after_create, :block ],
+ [ :after_save, :method ],
[ :after_save, :string ],
[ :after_save, :proc ],
[ :after_save, :object ],
@@ -267,42 +254,42 @@ class CallbacksTest < ActiveRecord::TestCase
david = CallbackDeveloper.find(1)
david.save
assert_equal [
+ [ :after_find, :method ],
[ :after_find, :string ],
[ :after_find, :proc ],
[ :after_find, :object ],
[ :after_find, :block ],
+ [ :after_initialize, :method ],
[ :after_initialize, :string ],
[ :after_initialize, :proc ],
[ :after_initialize, :object ],
[ :after_initialize, :block ],
+ [ :before_validation, :method ],
[ :before_validation, :string ],
[ :before_validation, :proc ],
[ :before_validation, :object ],
[ :before_validation, :block ],
- [ :before_validation_on_update, :string ],
- [ :before_validation_on_update, :proc ],
- [ :before_validation_on_update, :object ],
- [ :before_validation_on_update, :block ],
+ [ :after_validation, :method ],
[ :after_validation, :string ],
[ :after_validation, :proc ],
[ :after_validation, :object ],
[ :after_validation, :block ],
- [ :after_validation_on_update, :string ],
- [ :after_validation_on_update, :proc ],
- [ :after_validation_on_update, :object ],
- [ :after_validation_on_update, :block ],
+ [ :before_save, :method ],
[ :before_save, :string ],
[ :before_save, :proc ],
[ :before_save, :object ],
[ :before_save, :block ],
+ [ :before_update, :method ],
[ :before_update, :string ],
[ :before_update, :proc ],
[ :before_update, :object ],
[ :before_update, :block ],
+ [ :after_update, :method ],
[ :after_update, :string ],
[ :after_update, :proc ],
[ :after_update, :object ],
[ :after_update, :block ],
+ [ :after_save, :method ],
[ :after_save, :string ],
[ :after_save, :proc ],
[ :after_save, :object ],
@@ -314,18 +301,22 @@ class CallbacksTest < ActiveRecord::TestCase
david = CallbackDeveloper.find(1)
david.destroy
assert_equal [
+ [ :after_find, :method ],
[ :after_find, :string ],
[ :after_find, :proc ],
[ :after_find, :object ],
[ :after_find, :block ],
+ [ :after_initialize, :method ],
[ :after_initialize, :string ],
[ :after_initialize, :proc ],
[ :after_initialize, :object ],
[ :after_initialize, :block ],
+ [ :before_destroy, :method ],
[ :before_destroy, :string ],
[ :before_destroy, :proc ],
[ :before_destroy, :object ],
[ :before_destroy, :block ],
+ [ :after_destroy, :method ],
[ :after_destroy, :string ],
[ :after_destroy, :proc ],
[ :after_destroy, :object ],
@@ -337,10 +328,12 @@ class CallbacksTest < ActiveRecord::TestCase
david = CallbackDeveloper.find(1)
CallbackDeveloper.delete(david.id)
assert_equal [
+ [ :after_find, :method ],
[ :after_find, :string ],
[ :after_find, :proc ],
[ :after_find, :object ],
[ :after_find, :block ],
+ [ :after_initialize, :method ],
[ :after_initialize, :string ],
[ :after_initialize, :proc ],
[ :after_initialize, :object ],
@@ -407,14 +400,17 @@ class CallbacksTest < ActiveRecord::TestCase
CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] }
david.save
assert_equal [
+ [ :after_find, :method ],
[ :after_find, :string ],
[ :after_find, :proc ],
[ :after_find, :object ],
[ :after_find, :block ],
+ [ :after_initialize, :method ],
[ :after_initialize, :string ],
[ :after_initialize, :proc ],
[ :after_initialize, :object ],
[ :after_initialize, :block ],
+ [ :before_validation, :method ],
[ :before_validation, :string ],
[ :before_validation, :proc ],
[ :before_validation, :object ],
diff --git a/activerecord/test/cases/class_inheritable_attributes_test.rb b/activerecord/test/cases/class_inheritable_attributes_test.rb
deleted file mode 100644
index abeb63c591..0000000000
--- a/activerecord/test/cases/class_inheritable_attributes_test.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require 'test/unit'
-require "cases/helper"
-require 'active_support/core_ext/class/inheritable_attributes'
-
-class A
- include ClassInheritableAttributes
-end
-
-class B < A
- write_inheritable_array "first", [ :one, :two ]
-end
-
-class C < A
- write_inheritable_array "first", [ :three ]
-end
-
-class D < B
- write_inheritable_array "first", [ :four ]
-end
-
-
-class ClassInheritableAttributesTest < ActiveRecord::TestCase
- def test_first_level
- assert_equal [ :one, :two ], B.read_inheritable_attribute("first")
- assert_equal [ :three ], C.read_inheritable_attribute("first")
- end
-
- def test_second_level
- assert_equal [ :one, :two, :four ], D.read_inheritable_attribute("first")
- assert_equal [ :one, :two ], B.read_inheritable_attribute("first")
- end
-end
diff --git a/activerecord/test/cases/connection_test_mysql.rb b/activerecord/test/cases/connection_test_mysql.rb
index f79ee2f1f7..8e4842a1b6 100644
--- a/activerecord/test/cases/connection_test_mysql.rb
+++ b/activerecord/test/cases/connection_test_mysql.rb
@@ -41,6 +41,14 @@ class MysqlConnectionTest < ActiveRecord::TestCase
sleep 2
@connection.verify!
assert @connection.active?
+ end
+
+ # Test that MySQL allows multiple results for stored procedures
+ if Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
+ def test_multi_results
+ rows = ActiveRecord::Base.connection.select_rows('CALL ten();')
+ assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}"
+ end
end
private
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index d1e7caed89..aa09c7061f 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -12,8 +12,6 @@ require 'active_record/test_case'
require 'active_record/fixtures'
require 'connection'
-require 'cases/repair_helper'
-
begin
require 'ruby-debug'
rescue LoadError
diff --git a/activerecord/test/cases/lifecycle_test.rb b/activerecord/test/cases/lifecycle_test.rb
index 54fb3d8c39..aa7ce2ecb6 100644
--- a/activerecord/test/cases/lifecycle_test.rb
+++ b/activerecord/test/cases/lifecycle_test.rb
@@ -1,11 +1,9 @@
-require "cases/helper"
+require 'cases/helper'
require 'models/topic'
require 'models/developer'
require 'models/reply'
require 'models/minimalistic'
-class Topic; def after_find() end end
-class Developer; def after_find() end end
class SpecialDeveloper < Developer; end
class TopicManualObserver
@@ -43,6 +41,11 @@ class TopicObserver < ActiveRecord::Observer
def after_find(topic)
@topic = topic
end
+
+ # Create an after_save callback, so a notify_observer hook is created
+ # on :topic.
+ def after_save(nothing)
+ end
end
class MinimalisticObserver < ActiveRecord::Observer
@@ -159,34 +162,6 @@ class LifecycleTest < ActiveRecord::TestCase
assert_equal topic, observer.topic
end
- def test_after_find_is_not_created_if_its_not_used
- # use a fresh class so an observer can't have defined an
- # after_find on it
- model_class = Class.new(ActiveRecord::Base)
- observer_class = Class.new(ActiveRecord::Observer)
- observer_class.observe(model_class)
-
- observer = observer_class.instance
-
- assert !model_class.method_defined?(:after_find)
- end
-
- def test_after_find_is_not_clobbered_if_it_already_exists
- # use a fresh observer class so we can instantiate it (Observer is
- # a Singleton)
- model_class = Class.new(ActiveRecord::Base) do
- def after_find; end
- end
- original_method = model_class.instance_method(:after_find)
- observer_class = Class.new(ActiveRecord::Observer) do
- def after_find; end
- end
- observer_class.observe(model_class)
-
- observer = observer_class.instance
- assert_equal original_method, model_class.instance_method(:after_find)
- end
-
def test_invalid_observer
assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index d033c1e760..721792132c 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -68,24 +68,38 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
ship = pirate.create_ship(:name => 'Nights Dirty Lightning')
assert_no_difference('Ship.count') do
- pirate.update_attributes(:ship_attributes => { '_delete' => true })
+ pirate.update_attributes(:ship_attributes => { '_destroy' => true })
end
end
- def test_a_model_should_respond_to_underscore_delete_and_return_if_it_is_marked_for_destruction
+ def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction
ship = Ship.create!(:name => 'Nights Dirty Lightning')
- assert !ship._delete
+ assert !ship._destroy
ship.mark_for_destruction
- assert ship._delete
+ assert ship._destroy
+ end
+
+ def test_underscore_delete_is_deprecated
+ ActiveSupport::Deprecation.expects(:warn)
+ ship = Ship.create!(:name => 'Nights Dirty Lightning')
+ ship._delete
end
end
class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
+ include AssertRaiseWithMessage
+
def setup
@pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
end
+ def test_should_raise_argument_error_if_trying_to_build_polymorphic_belongs_to
+ assert_raise_with_message ArgumentError, "Cannot build association looter. Are you trying to build a polymorphic one-to-one association?" do
+ Treasure.new(:name => 'pearl', :looter_attributes => {:catchphrase => "Arrr"})
+ end
+ end
+
def test_should_define_an_attribute_writer_method_for_the_association
assert_respond_to @pirate, :ship_attributes=
end
@@ -98,9 +112,9 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
end
- def test_should_not_build_a_new_record_if_there_is_no_id_and_delete_is_truthy
+ def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
@ship.destroy
- @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_delete => '1' }
+ @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
assert_nil @pirate.ship
end
@@ -120,8 +134,8 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
assert_equal 'Nights Dirty Lightning', @ship.name
end
- def test_should_not_replace_an_existing_record_if_there_is_no_id_and_delete_is_truthy
- @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_delete => '1' }
+ def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
+ @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
assert_equal @ship, @pirate.ship
assert_equal 'Nights Dirty Lightning', @pirate.ship.name
@@ -148,29 +162,29 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
end
- def test_should_delete_an_existing_record_if_there_is_a_matching_id_and_delete_is_truthy
+ def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
@pirate.ship.destroy
[1, '1', true, 'true'].each do |truth|
@pirate.reload.create_ship(:name => 'Mister Pablo')
assert_difference('Ship.count', -1) do
- @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => truth })
+ @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => truth })
end
end
end
- def test_should_not_delete_an_existing_record_if_delete_is_not_truthy
+ def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
[nil, '0', 0, 'false', false].each do |not_truth|
assert_no_difference('Ship.count') do
- @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => not_truth })
+ @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => not_truth })
end
end
end
- def test_should_not_delete_an_existing_record_if_allow_destroy_is_false
+ def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
Pirate.accepts_nested_attributes_for :ship, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? }
assert_no_difference('Ship.count') do
- @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_delete => '1' })
+ @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => '1' })
end
Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
@@ -193,7 +207,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
assert_no_difference('Ship.count') do
- @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_delete => '1' } }
+ @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_destroy => '1' } }
end
assert_difference('Ship.count', -1) do
@pirate.save
@@ -224,9 +238,9 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
assert_equal 'Arr', @ship.pirate.catchphrase
end
- def test_should_not_build_a_new_record_if_there_is_no_id_and_delete_is_truthy
+ def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
@pirate.destroy
- @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_delete => '1' }
+ @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
assert_nil @ship.pirate
end
@@ -246,8 +260,8 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
assert_equal 'Aye', @pirate.catchphrase
end
- def test_should_not_replace_an_existing_record_if_there_is_no_id_and_delete_is_truthy
- @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_delete => '1' }
+ def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
+ @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
assert_equal @pirate, @ship.pirate
assert_equal 'Aye', @ship.pirate.catchphrase
@@ -274,29 +288,29 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
assert_equal 'Arr', @ship.pirate.catchphrase
end
- def test_should_delete_an_existing_record_if_there_is_a_matching_id_and_delete_is_truthy
+ def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
@ship.pirate.destroy
[1, '1', true, 'true'].each do |truth|
@ship.reload.create_pirate(:catchphrase => 'Arr')
assert_difference('Pirate.count', -1) do
- @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => truth })
+ @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => truth })
end
end
end
- def test_should_not_delete_an_existing_record_if_delete_is_not_truthy
+ def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
[nil, '0', 0, 'false', false].each do |not_truth|
assert_no_difference('Pirate.count') do
- @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => not_truth })
+ @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => not_truth })
end
end
end
- def test_should_not_delete_an_existing_record_if_allow_destroy_is_false
+ def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
Ship.accepts_nested_attributes_for :pirate, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? }
assert_no_difference('Pirate.count') do
- @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_delete => '1' })
+ @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => '1' })
end
Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
@@ -312,7 +326,7 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
assert_no_difference('Pirate.count') do
- @ship.attributes = { :pirate_attributes => { :id => @ship.pirate.id, '_delete' => true } }
+ @ship.attributes = { :pirate_attributes => { :id => @ship.pirate.id, '_destroy' => true } }
end
assert_difference('Pirate.count', -1) { @ship.save }
end
@@ -380,18 +394,18 @@ module NestedAttributesOnACollectionAssociationTests
assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
end
- def test_should_not_assign_delete_key_to_a_record
+ def test_should_not_assign_destroy_key_to_a_record
assert_nothing_raised ActiveRecord::UnknownAttributeError do
- @pirate.send(association_setter, { 'foo' => { '_delete' => '0' }})
+ @pirate.send(association_setter, { 'foo' => { '_destroy' => '0' }})
end
end
- def test_should_ignore_new_associated_records_with_truthy_delete_attribute
+ def test_should_ignore_new_associated_records_with_truthy_destroy_attribute
@pirate.send(@association_name).destroy_all
@pirate.reload.attributes = {
association_getter => {
'foo' => { :name => 'Grace OMalley' },
- 'bar' => { :name => 'Privateers Greed', '_delete' => '1' }
+ 'bar' => { :name => 'Privateers Greed', '_destroy' => '1' }
}
}
@@ -443,7 +457,7 @@ module NestedAttributesOnACollectionAssociationTests
['1', 1, 'true', true].each do |true_variable|
record = @pirate.reload.send(@association_name).create!(:name => 'Grace OMalley')
@pirate.send(association_setter,
- @alternate_params[association_getter].merge('baz' => { :id => record.id, '_delete' => true_variable })
+ @alternate_params[association_getter].merge('baz' => { :id => record.id, '_destroy' => true_variable })
)
assert_difference('@pirate.send(@association_name).count', -1) do
@@ -454,7 +468,7 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_not_destroy_the_associated_model_with_a_non_truthy_argument
[nil, '', '0', 0, 'false', false].each do |false_variable|
- @alternate_params[association_getter]['foo']['_delete'] = false_variable
+ @alternate_params[association_getter]['foo']['_destroy'] = false_variable
assert_no_difference('@pirate.send(@association_name).count') do
@pirate.update_attributes(@alternate_params)
end
@@ -463,7 +477,7 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
assert_no_difference('@pirate.send(@association_name).count') do
- @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_delete' => true }))
+ @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_destroy' => true }))
end
assert_difference('@pirate.send(@association_name).count', -1) { @pirate.save }
end
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index a164f5e060..0eb2da720e 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -176,9 +176,9 @@ class ReflectionTest < ActiveRecord::TestCase
def test_reflection_of_all_associations
# FIXME these assertions bust a lot
- assert_equal 31, Firm.reflect_on_all_associations.size
- assert_equal 24, Firm.reflect_on_all_associations(:has_many).size
- assert_equal 7, Firm.reflect_on_all_associations(:has_one).size
+ assert_equal 35, Firm.reflect_on_all_associations.size
+ assert_equal 26, Firm.reflect_on_all_associations(:has_many).size
+ assert_equal 9, Firm.reflect_on_all_associations(:has_one).size
assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
end
diff --git a/activerecord/test/cases/repair_helper.rb b/activerecord/test/cases/repair_helper.rb
deleted file mode 100644
index 80d04010d6..0000000000
--- a/activerecord/test/cases/repair_helper.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-module ActiveRecord
- module Testing
- module RepairHelper
- extend ActiveSupport::Concern
-
- module Toolbox
- def self.record_validations(*model_classes)
- model_classes.inject({}) do |repair, klass|
- repair[klass] ||= {}
- [:validate, :validate_on_create, :validate_on_update].each do |callback|
- the_callback = klass.instance_variable_get("@#{callback.to_s}_callbacks")
- repair[klass][callback] = (the_callback.nil? ? nil : the_callback.dup)
- end
- repair
- end
- end
-
- def self.reset_validations(recorded)
- recorded.each do |klass, repairs|
- [:validate, :validate_on_create, :validate_on_update].each do |callback|
- klass.instance_variable_set("@#{callback.to_s}_callbacks", repairs[callback])
- end
- end
- end
- end
-
- module ClassMethods
- def repair_validations(*model_classes)
- setup do
- @validation_repairs = ActiveRecord::Testing::RepairHelper::Toolbox.record_validations(*model_classes)
- end
- teardown do
- ActiveRecord::Testing::RepairHelper::Toolbox.reset_validations(@validation_repairs)
- end
- end
- end
-
- def repair_validations(*model_classes, &block)
- validation_repairs = ActiveRecord::Testing::RepairHelper::Toolbox.record_validations(*model_classes)
- return block.call
- ensure
- ActiveRecord::Testing::RepairHelper::Toolbox.reset_validations(validation_repairs)
- end
- end
- end
-end
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index f6533b5396..aca70b4238 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -382,28 +382,28 @@ class TransactionTest < ActiveRecord::TestCase
private
def add_exception_raising_after_save_callback_to_topic
- Topic.class_eval { def after_save() raise "Make the transaction rollback" end }
+ Topic.class_eval "def after_save_for_transaction; raise 'Make the transaction rollback' end"
end
def remove_exception_raising_after_save_callback_to_topic
- Topic.class_eval { remove_method :after_save }
+ Topic.class_eval "def after_save_for_transaction; end"
end
def add_exception_raising_after_create_callback_to_topic
- Topic.class_eval { def after_create() raise "Make the transaction rollback" end }
+ Topic.class_eval "def after_create_for_transaction; raise 'Make the transaction rollback' end"
end
def remove_exception_raising_after_create_callback_to_topic
- Topic.class_eval { remove_method :after_create }
+ Topic.class_eval "def after_create_for_transaction; end"
end
%w(validation save destroy).each do |filter|
define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
- Topic.class_eval "def before_#{filter}() Book.create; false end"
+ Topic.class_eval "def before_#{filter}_for_transaction() Book.create; false end"
end
define_method("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
- Topic.class_eval "remove_method :before_#{filter}"
+ Topic.class_eval "def before_#{filter}_for_transaction; end"
end
end
end
diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
index 29c10de4fe..3794a0ebb9 100644
--- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
@@ -2,9 +2,9 @@ require "cases/helper"
require 'models/topic'
require 'models/reply'
-class I18nGenerateMessageValidationTest < Test::Unit::TestCase
+class I18nGenerateMessageValidationTest < ActiveRecord::TestCase
def setup
- reset_callbacks Topic
+ Topic.reset_callbacks(:validate)
@topic = Topic.new
I18n.backend.store_translations :'en', {
:activerecord => {
@@ -17,14 +17,6 @@ class I18nGenerateMessageValidationTest < Test::Unit::TestCase
}
end
- def reset_callbacks(*models)
- models.each do |model|
- model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- end
- end
-
# validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value)
def test_generate_message_inclusion_with_default_message
assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title')
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 73d9c7249c..252138c0d6 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -4,7 +4,7 @@ require 'models/reply'
class I18nValidationTest < ActiveRecord::TestCase
def setup
- reset_callbacks Topic
+ Topic.reset_callbacks(:validate)
@topic = Topic.new
@old_load_path, @old_backend = I18n.load_path, I18n.backend
I18n.load_path.clear
@@ -13,7 +13,7 @@ class I18nValidationTest < ActiveRecord::TestCase
end
def teardown
- reset_callbacks Topic
+ Topic.reset_callbacks(:validate)
I18n.load_path.replace @old_load_path
I18n.backend = @old_backend
end
@@ -30,14 +30,6 @@ class I18nValidationTest < ActiveRecord::TestCase
end
end
- def reset_callbacks(*models)
- models.each do |model|
- model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- end
- end
-
def test_percent_s_interpolation_syntax_in_error_messages_was_deprecated
assert_not_deprecated do
default = "%s interpolation syntax was deprecated"
@@ -710,9 +702,9 @@ class I18nValidationTest < ActiveRecord::TestCase
end
end
-class ActiveRecordValidationsGenerateMessageI18nTests < ActiveSupport::TestCase
+class ActiveRecordValidationsGenerateMessageI18nTests < ActiveRecord::TestCase
+
def setup
- reset_callbacks Topic
@topic = Topic.new
I18n.backend.store_translations :'en', {
:activerecord => {
@@ -743,14 +735,6 @@ class ActiveRecordValidationsGenerateMessageI18nTests < ActiveSupport::TestCase
}
end
- def reset_callbacks(*models)
- models.each do |model|
- model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
- end
- end
-
# validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value)
def test_generate_message_inclusion_with_default_message
assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title')
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index a4e874e5e6..5cdb623eef 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -149,24 +149,24 @@ class ValidationsTest < ActiveRecord::TestCase
end
def test_validates_length_with_globally_modified_error_message
- ActiveSupport::Deprecation.silence do
- ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}'
- end
+ defaults = ActiveSupport::Deprecation.silence { ActiveRecord::Errors.default_error_messages }
+ original_message = defaults[:too_short]
+ defaults[:too_short] = 'tu est trops petit hombre {{count}}'
Topic.validates_length_of :title, :minimum => 10
t = Topic.create(:title => 'too short')
assert !t.valid?
assert_equal ['tu est trops petit hombre 10'], t.errors[:title]
+
+ ensure
+ defaults[:too_short] = original_message
end
def test_validates_acceptance_of_as_database_column
- repair_validations(Reply) do
- Reply.validates_acceptance_of(:author_name)
-
- reply = Reply.create("author_name" => "Dan Brown")
- assert_equal "Dan Brown", reply["author_name"]
- end
+ Topic.validates_acceptance_of(:author_name)
+ topic = Topic.create("author_name" => "Dan Brown")
+ assert_equal "Dan Brown", topic["author_name"]
end
def test_deprecated_validation_instance_methods
diff --git a/activerecord/test/fixtures/accounts.yml b/activerecord/test/fixtures/accounts.yml
index b2d0191900..32583042a8 100644
--- a/activerecord/test/fixtures/accounts.yml
+++ b/activerecord/test/fixtures/accounts.yml
@@ -2,6 +2,7 @@ signals37:
id: 1
firm_id: 1
credit_limit: 50
+ firm_name: 37signals
unknown:
id: 2
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index f264f980d6..7cbc6e803f 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -94,8 +94,9 @@ class Author < ActiveRecord::Base
belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress"
attr_accessor :post_log
+ after_initialize :set_post_log
- def after_initialize
+ def set_post_log
@post_log = []
end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index ab09f88a9f..b1a3930e4e 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -78,7 +78,12 @@ class Firm < Company
# added order by id as in fixtures there are two accounts for Rails Core
# Oracle tests were failing because of that as the second fixture was selected
has_one :account_using_primary_key, :primary_key => "firm_id", :class_name => "Account", :order => "id"
+ has_one :account_using_foreign_and_primary_keys, :foreign_key => "firm_name", :primary_key => "name", :class_name => "Account"
has_one :deletable_account, :foreign_key => "firm_id", :class_name => "Account", :dependent => :delete
+
+ has_one :unautosaved_account, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
+ has_many :accounts
+ has_many :unautosaved_accounts, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
end
class DependentFirm < Company
@@ -111,6 +116,8 @@ class Client < Company
true
end
+ before_destroy :overwrite_to_raise
+
# Used to test that read and question methods are not generated for these attributes
def ruby_type
read_attribute :ruby_type
@@ -120,6 +127,9 @@ class Client < Company
query_attribute :rating
end
+ def overwrite_to_raise
+ end
+
class << self
private
@@ -144,6 +154,7 @@ end
class Account < ActiveRecord::Base
belongs_to :firm
+ belongs_to :unautosaved_firm, :foreign_key => "firm_id", :class_name => "Firm", :autosave => false
def self.destroyed_account_ids
@destroyed_account_ids ||= Hash.new { |h,k| h[k] = [] }
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
index acf53fce8b..3d7c4bc48a 100644
--- a/activerecord/test/models/pirate.rb
+++ b/activerecord/test/models/pirate.rb
@@ -1,6 +1,8 @@
class Pirate < ActiveRecord::Base
- belongs_to :parrot
- has_and_belongs_to_many :parrots
+ belongs_to :parrot, :validate => true
+ belongs_to :non_validated_parrot, :class_name => 'Parrot'
+ has_and_belongs_to_many :parrots, :validate => true
+ has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot'
has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot",
:before_add => :log_before_add,
:after_add => :log_after_add,
@@ -16,7 +18,8 @@ class Pirate < ActiveRecord::Base
has_many :treasure_estimates, :through => :treasures, :source => :price_estimates
# These both have :autosave enabled because accepts_nested_attributes_for is used on them.
- has_one :ship
+ has_one :ship, :validate => true
+ has_one :non_validated_ship, :class_name => 'Ship'
has_many :birds
has_many :birds_with_method_callbacks, :class_name => "Bird",
:before_add => :log_before_add,
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
index 422b12dc83..416032cb75 100644
--- a/activerecord/test/models/project.rb
+++ b/activerecord/test/models/project.rb
@@ -22,8 +22,9 @@ class Project < ActiveRecord::Base
has_and_belongs_to_many :well_payed_salary_groups, :class_name => "Developer", :group => "developers.salary", :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary"
attr_accessor :developers_log
+ after_initialize :set_developers_log
- def after_initialize
+ def set_developers_log
@developers_log = []
end
diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb
index f5906dedd1..ba5a1d1d01 100644
--- a/activerecord/test/models/reply.rb
+++ b/activerecord/test/models/reply.rb
@@ -8,13 +8,13 @@ class Reply < Topic
has_many :replies, :class_name => "SillyReply", :dependent => :destroy, :foreign_key => "parent_id"
validate :errors_on_empty_content
- validate_on_create :title_is_wrong_create
+ validate :title_is_wrong_create, :on => :create
attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read, :parent_title
validate :check_empty_title
- validate_on_create :check_content_mismatch
- validate_on_update :check_wrong_update
+ validate :check_content_mismatch, :on => :create
+ validate :check_wrong_update, :on => :update
def check_empty_title
errors[:title] << "Empty" unless attribute_present?("title")
@@ -47,4 +47,4 @@ module Web
class Reply < Web::Topic
belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true, :class_name => 'Web::Topic'
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb
index 06759d64b8..d0df951622 100644
--- a/activerecord/test/models/ship.rb
+++ b/activerecord/test/models/ship.rb
@@ -1,7 +1,7 @@
class Ship < ActiveRecord::Base
self.record_timestamps = false
- belongs_to :pirate
+ belongs_to :pirate, :validate => true
has_many :parts, :class_name => 'ShipPart', :autosave => true
accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 9594dc300a..baca4972cb 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -52,6 +52,15 @@ class Topic < ActiveRecord::Base
id
end
+ before_validation :before_validation_for_transaction
+ before_save :before_save_for_transaction
+ before_destroy :before_destroy_for_transaction
+
+ after_save :after_save_for_transaction
+ after_create :after_create_for_transaction
+
+ after_initialize :set_email_address
+
protected
def approved=(val)
@custom_approved = val
@@ -66,15 +75,21 @@ class Topic < ActiveRecord::Base
self.class.delete_all "parent_id = #{id}"
end
- def after_initialize
+ def set_email_address
if self.new_record?
self.author_email_address = 'test@test.com'
end
end
+
+ def before_validation_for_transaction; end
+ def before_save_for_transaction; end
+ def before_destroy_for_transaction; end
+ def after_save_for_transaction; end
+ def after_create_for_transaction; end
end
module Web
class Topic < ActiveRecord::Base
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id", :class_name => 'Web::Reply'
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/treasure.rb b/activerecord/test/models/treasure.rb
index 97c690c110..2a98e74f2c 100644
--- a/activerecord/test/models/treasure.rb
+++ b/activerecord/test/models/treasure.rb
@@ -3,4 +3,6 @@ class Treasure < ActiveRecord::Base
belongs_to :looter, :polymorphic => true
has_many :price_estimates, :as => :estimate_of
+
+ accepts_nested_attributes_for :looter
end
diff --git a/activerecord/test/schema/mysql_specific_schema.rb b/activerecord/test/schema/mysql_specific_schema.rb
index f44c33ae67..c78d99f4af 100644
--- a/activerecord/test/schema/mysql_specific_schema.rb
+++ b/activerecord/test/schema/mysql_specific_schema.rb
@@ -9,4 +9,16 @@ ActiveRecord::Schema.define do
t.text :medium_text, :limit => 16777215
t.text :long_text, :limit => 2147483647
end
+
+ ActiveRecord::Base.connection.execute <<-SQL
+DROP PROCEDURE IF EXISTS ten;
+SQL
+
+ ActiveRecord::Base.connection.execute <<-SQL
+CREATE PROCEDURE ten() SQL SECURITY INVOKER
+BEGIN
+ select 10;
+END
+SQL
+
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 9ab4cf6f43..15e5e12d03 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -22,6 +22,7 @@ ActiveRecord::Schema.define do
# unless the ordering matters. In which case, define them below
create_table :accounts, :force => true do |t|
t.integer :firm_id
+ t.string :firm_name
t.integer :credit_limit
end