activerecord 2.3.18 → 3.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +105 -34
- data/examples/performance.rb +3 -39
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +81 -47
- data/lib/active_record/aggregations.rb +1 -3
- data/lib/active_record/association_preload.rb +39 -54
- data/lib/active_record/associations.rb +262 -419
- data/lib/active_record/associations/association_collection.rb +85 -100
- data/lib/active_record/associations/association_proxy.rb +20 -18
- data/lib/active_record/associations/belongs_to_association.rb +8 -8
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -35
- data/lib/active_record/associations/has_many_association.rb +11 -19
- data/lib/active_record/associations/has_many_through_association.rb +30 -183
- data/lib/active_record/associations/has_one_association.rb +10 -10
- data/lib/active_record/associations/has_one_through_association.rb +13 -11
- data/lib/active_record/associations/through_association_scope.rb +153 -0
- data/lib/active_record/attribute_methods.rb +17 -370
- data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
- data/lib/active_record/attribute_methods/dirty.rb +87 -0
- data/lib/active_record/attribute_methods/primary_key.rb +44 -0
- data/lib/active_record/attribute_methods/query.rb +37 -0
- data/lib/active_record/attribute_methods/read.rb +116 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +60 -0
- data/lib/active_record/attribute_methods/write.rb +37 -0
- data/lib/active_record/autosave_association.rb +20 -41
- data/lib/active_record/base.rb +357 -1180
- data/lib/active_record/batches.rb +10 -16
- data/lib/active_record/callbacks.rb +66 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +17 -13
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +5 -25
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -72
- data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -49
- data/lib/active_record/connection_adapters/mysql_adapter.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +84 -46
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +9 -3
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +34 -65
- data/lib/active_record/fixtures.rb +21 -25
- data/lib/active_record/locale/en.yml +9 -27
- data/lib/active_record/locking/optimistic.rb +16 -48
- data/lib/active_record/migration.rb +59 -46
- data/lib/active_record/named_scope.rb +85 -92
- data/lib/active_record/nested_attributes.rb +18 -24
- data/lib/active_record/observer.rb +18 -94
- data/lib/active_record/railtie.rb +83 -0
- data/lib/active_record/railties/controller_runtime.rb +38 -0
- data/lib/active_record/railties/databases.rake +477 -0
- data/lib/active_record/railties/subscriber.rb +27 -0
- data/lib/active_record/reflection.rb +2 -15
- data/lib/active_record/relation.rb +339 -0
- data/lib/active_record/relation/calculations.rb +259 -0
- data/lib/active_record/relation/finder_methods.rb +315 -0
- data/lib/active_record/relation/predicate_builder.rb +46 -0
- data/lib/active_record/relation/query_methods.rb +218 -0
- data/lib/active_record/relation/spawn_methods.rb +102 -0
- data/lib/active_record/schema_dumper.rb +10 -6
- data/lib/active_record/serialization.rb +31 -74
- data/lib/active_record/serializers/xml_serializer.rb +33 -121
- data/lib/active_record/session_store.rb +1 -9
- data/lib/active_record/test_case.rb +1 -3
- data/lib/active_record/timestamp.rb +7 -5
- data/lib/active_record/transactions.rb +9 -9
- data/lib/active_record/validations.rb +51 -1100
- data/lib/active_record/validations/associated.rb +47 -0
- data/lib/active_record/validations/uniqueness.rb +181 -0
- data/lib/active_record/version.rb +3 -3
- data/lib/generators/active_record.rb +30 -0
- data/lib/generators/active_record/migration/migration_generator.rb +25 -0
- data/lib/generators/active_record/migration/templates/migration.rb +11 -0
- data/lib/generators/active_record/model/model_generator.rb +33 -0
- data/lib/generators/active_record/model/templates/migration.rb +16 -0
- data/lib/generators/active_record/model/templates/model.rb +5 -0
- data/lib/generators/active_record/observer/observer_generator.rb +15 -0
- data/lib/generators/active_record/observer/templates/observer.rb +2 -0
- data/lib/generators/active_record/session_migration/session_migration_generator.rb +24 -0
- data/lib/generators/active_record/session_migration/templates/migration.rb +16 -0
- metadata +67 -325
- data/RUNNING_UNIT_TESTS +0 -36
- data/Rakefile +0 -268
- data/install.rb +0 -30
- data/lib/active_record/calculations.rb +0 -321
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -57
- data/lib/active_record/dirty.rb +0 -183
- data/lib/active_record/serializers/json_serializer.rb +0 -91
- data/lib/activerecord.rb +0 -2
- data/test/assets/example.log +0 -1
- data/test/assets/flowers.jpg +0 -0
- data/test/cases/aaa_create_tables_test.rb +0 -24
- data/test/cases/active_schema_test_mysql.rb +0 -122
- data/test/cases/active_schema_test_postgresql.rb +0 -24
- data/test/cases/adapter_test.rb +0 -144
- data/test/cases/aggregations_test.rb +0 -167
- data/test/cases/ar_schema_test.rb +0 -32
- data/test/cases/associations/belongs_to_associations_test.rb +0 -438
- data/test/cases/associations/callbacks_test.rb +0 -161
- data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
- data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
- data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
- data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
- data/test/cases/associations/eager_singularization_test.rb +0 -145
- data/test/cases/associations/eager_test.rb +0 -852
- data/test/cases/associations/extension_test.rb +0 -62
- data/test/cases/associations/habtm_join_table_test.rb +0 -56
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
- data/test/cases/associations/has_many_associations_test.rb +0 -1273
- data/test/cases/associations/has_many_through_associations_test.rb +0 -360
- data/test/cases/associations/has_one_associations_test.rb +0 -330
- data/test/cases/associations/has_one_through_associations_test.rb +0 -209
- data/test/cases/associations/inner_join_association_test.rb +0 -93
- data/test/cases/associations/inverse_associations_test.rb +0 -566
- data/test/cases/associations/join_model_test.rb +0 -712
- data/test/cases/associations_test.rb +0 -282
- data/test/cases/attribute_methods_test.rb +0 -305
- data/test/cases/autosave_association_test.rb +0 -1218
- data/test/cases/base_test.rb +0 -2166
- data/test/cases/batches_test.rb +0 -81
- data/test/cases/binary_test.rb +0 -30
- data/test/cases/calculations_test.rb +0 -360
- data/test/cases/callbacks_observers_test.rb +0 -38
- data/test/cases/callbacks_test.rb +0 -438
- data/test/cases/class_inheritable_attributes_test.rb +0 -32
- data/test/cases/column_alias_test.rb +0 -17
- data/test/cases/column_definition_test.rb +0 -70
- data/test/cases/connection_pool_test.rb +0 -25
- data/test/cases/connection_test_firebird.rb +0 -8
- data/test/cases/connection_test_mysql.rb +0 -65
- data/test/cases/copy_table_test_sqlite.rb +0 -80
- data/test/cases/counter_cache_test.rb +0 -84
- data/test/cases/database_statements_test.rb +0 -12
- data/test/cases/datatype_test_postgresql.rb +0 -204
- data/test/cases/date_time_test.rb +0 -37
- data/test/cases/default_test_firebird.rb +0 -16
- data/test/cases/defaults_test.rb +0 -111
- data/test/cases/deprecated_finder_test.rb +0 -30
- data/test/cases/dirty_test.rb +0 -316
- data/test/cases/finder_respond_to_test.rb +0 -76
- data/test/cases/finder_test.rb +0 -1098
- data/test/cases/fixtures_test.rb +0 -661
- data/test/cases/helper.rb +0 -68
- data/test/cases/i18n_test.rb +0 -46
- data/test/cases/inheritance_test.rb +0 -262
- data/test/cases/invalid_date_test.rb +0 -24
- data/test/cases/json_serialization_test.rb +0 -219
- data/test/cases/lifecycle_test.rb +0 -193
- data/test/cases/locking_test.rb +0 -350
- data/test/cases/method_scoping_test.rb +0 -704
- data/test/cases/migration_test.rb +0 -1649
- data/test/cases/migration_test_firebird.rb +0 -124
- data/test/cases/mixin_test.rb +0 -96
- data/test/cases/modules_test.rb +0 -109
- data/test/cases/multiple_db_test.rb +0 -85
- data/test/cases/named_scope_test.rb +0 -372
- data/test/cases/nested_attributes_test.rb +0 -840
- data/test/cases/pk_test.rb +0 -119
- data/test/cases/pooled_connections_test.rb +0 -103
- data/test/cases/query_cache_test.rb +0 -129
- data/test/cases/readonly_test.rb +0 -107
- data/test/cases/reflection_test.rb +0 -234
- data/test/cases/reload_models_test.rb +0 -22
- data/test/cases/repair_helper.rb +0 -50
- data/test/cases/reserved_word_test_mysql.rb +0 -176
- data/test/cases/sanitize_test.rb +0 -25
- data/test/cases/schema_authorization_test_postgresql.rb +0 -75
- data/test/cases/schema_dumper_test.rb +0 -211
- data/test/cases/schema_test_postgresql.rb +0 -178
- data/test/cases/serialization_test.rb +0 -47
- data/test/cases/sp_test_mysql.rb +0 -16
- data/test/cases/synonym_test_oracle.rb +0 -17
- data/test/cases/timestamp_test.rb +0 -75
- data/test/cases/transactions_test.rb +0 -543
- data/test/cases/unconnected_test.rb +0 -32
- data/test/cases/validations_i18n_test.rb +0 -925
- data/test/cases/validations_test.rb +0 -1684
- data/test/cases/xml_serialization_test.rb +0 -240
- data/test/cases/yaml_serialization_test.rb +0 -11
- data/test/config.rb +0 -5
- data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
- data/test/connections/jdbc_jdbch2/connection.rb +0 -18
- data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
- data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
- data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
- data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
- data/test/connections/native_db2/connection.rb +0 -25
- data/test/connections/native_firebird/connection.rb +0 -26
- data/test/connections/native_frontbase/connection.rb +0 -27
- data/test/connections/native_mysql/connection.rb +0 -25
- data/test/connections/native_openbase/connection.rb +0 -21
- data/test/connections/native_oracle/connection.rb +0 -27
- data/test/connections/native_postgresql/connection.rb +0 -21
- data/test/connections/native_sqlite/connection.rb +0 -25
- data/test/connections/native_sqlite3/connection.rb +0 -25
- data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
- data/test/connections/native_sybase/connection.rb +0 -23
- data/test/fixtures/accounts.yml +0 -29
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author_addresses.yml +0 -5
- data/test/fixtures/author_favorites.yml +0 -4
- data/test/fixtures/authors.yml +0 -9
- data/test/fixtures/binaries.yml +0 -132
- data/test/fixtures/books.yml +0 -7
- data/test/fixtures/categories.yml +0 -14
- data/test/fixtures/categories/special_categories.yml +0 -9
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
- data/test/fixtures/categories_ordered.yml +0 -7
- data/test/fixtures/categories_posts.yml +0 -23
- data/test/fixtures/categorizations.yml +0 -17
- data/test/fixtures/clubs.yml +0 -6
- data/test/fixtures/comments.yml +0 -59
- data/test/fixtures/companies.yml +0 -56
- data/test/fixtures/computers.yml +0 -4
- data/test/fixtures/courses.yml +0 -7
- data/test/fixtures/customers.yml +0 -26
- data/test/fixtures/developers.yml +0 -21
- data/test/fixtures/developers_projects.yml +0 -17
- data/test/fixtures/edges.yml +0 -6
- data/test/fixtures/entrants.yml +0 -14
- data/test/fixtures/faces.yml +0 -11
- data/test/fixtures/fk_test_has_fk.yml +0 -3
- data/test/fixtures/fk_test_has_pk.yml +0 -2
- data/test/fixtures/funny_jokes.yml +0 -10
- data/test/fixtures/interests.yml +0 -33
- data/test/fixtures/items.yml +0 -4
- data/test/fixtures/jobs.yml +0 -7
- data/test/fixtures/legacy_things.yml +0 -3
- data/test/fixtures/mateys.yml +0 -4
- data/test/fixtures/member_types.yml +0 -6
- data/test/fixtures/members.yml +0 -6
- data/test/fixtures/memberships.yml +0 -20
- data/test/fixtures/men.yml +0 -5
- data/test/fixtures/minimalistics.yml +0 -2
- data/test/fixtures/mixed_case_monkeys.yml +0 -6
- data/test/fixtures/mixins.yml +0 -29
- data/test/fixtures/movies.yml +0 -7
- data/test/fixtures/naked/csv/accounts.csv +0 -1
- data/test/fixtures/naked/yml/accounts.yml +0 -1
- data/test/fixtures/naked/yml/companies.yml +0 -1
- data/test/fixtures/naked/yml/courses.yml +0 -1
- data/test/fixtures/organizations.yml +0 -5
- data/test/fixtures/owners.yml +0 -7
- data/test/fixtures/parrots.yml +0 -27
- data/test/fixtures/parrots_pirates.yml +0 -7
- data/test/fixtures/people.yml +0 -15
- data/test/fixtures/pets.yml +0 -14
- data/test/fixtures/pirates.yml +0 -9
- data/test/fixtures/polymorphic_designs.yml +0 -19
- data/test/fixtures/polymorphic_prices.yml +0 -19
- data/test/fixtures/posts.yml +0 -52
- data/test/fixtures/price_estimates.yml +0 -7
- data/test/fixtures/projects.yml +0 -7
- data/test/fixtures/readers.yml +0 -9
- data/test/fixtures/references.yml +0 -17
- data/test/fixtures/reserved_words/distinct.yml +0 -5
- data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
- data/test/fixtures/reserved_words/group.yml +0 -14
- data/test/fixtures/reserved_words/select.yml +0 -8
- data/test/fixtures/reserved_words/values.yml +0 -7
- data/test/fixtures/ships.yml +0 -5
- data/test/fixtures/sponsors.yml +0 -9
- data/test/fixtures/subscribers.yml +0 -7
- data/test/fixtures/subscriptions.yml +0 -12
- data/test/fixtures/taggings.yml +0 -28
- data/test/fixtures/tags.yml +0 -7
- data/test/fixtures/tasks.yml +0 -7
- data/test/fixtures/tees.yml +0 -4
- data/test/fixtures/ties.yml +0 -4
- data/test/fixtures/topics.yml +0 -42
- data/test/fixtures/toys.yml +0 -4
- data/test/fixtures/treasures.yml +0 -10
- data/test/fixtures/vertices.yml +0 -4
- data/test/fixtures/warehouse-things.yml +0 -3
- data/test/fixtures/zines.yml +0 -5
- data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
- data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
- data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
- data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
- data/test/migrations/duplicate/3_foo.rb +0 -7
- data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
- data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
- data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
- data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
- data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
- data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
- data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
- data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
- data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
- data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
- data/test/migrations/missing/1_people_have_last_names.rb +0 -9
- data/test/migrations/missing/3_we_need_reminders.rb +0 -12
- data/test/migrations/missing/4_innocent_jointable.rb +0 -12
- data/test/migrations/valid/1_people_have_last_names.rb +0 -9
- data/test/migrations/valid/2_we_need_reminders.rb +0 -12
- data/test/migrations/valid/3_innocent_jointable.rb +0 -12
- data/test/models/author.rb +0 -151
- data/test/models/auto_id.rb +0 -4
- data/test/models/binary.rb +0 -2
- data/test/models/bird.rb +0 -9
- data/test/models/book.rb +0 -4
- data/test/models/categorization.rb +0 -5
- data/test/models/category.rb +0 -34
- data/test/models/citation.rb +0 -6
- data/test/models/club.rb +0 -13
- data/test/models/column_name.rb +0 -3
- data/test/models/comment.rb +0 -29
- data/test/models/company.rb +0 -173
- data/test/models/company_in_module.rb +0 -78
- data/test/models/computer.rb +0 -3
- data/test/models/contact.rb +0 -16
- data/test/models/contract.rb +0 -5
- data/test/models/course.rb +0 -3
- data/test/models/customer.rb +0 -73
- data/test/models/default.rb +0 -2
- data/test/models/developer.rb +0 -101
- data/test/models/edge.rb +0 -5
- data/test/models/entrant.rb +0 -3
- data/test/models/essay.rb +0 -3
- data/test/models/event.rb +0 -3
- data/test/models/event_author.rb +0 -8
- data/test/models/face.rb +0 -7
- data/test/models/guid.rb +0 -2
- data/test/models/interest.rb +0 -5
- data/test/models/invoice.rb +0 -4
- data/test/models/item.rb +0 -7
- data/test/models/job.rb +0 -5
- data/test/models/joke.rb +0 -3
- data/test/models/keyboard.rb +0 -3
- data/test/models/legacy_thing.rb +0 -3
- data/test/models/line_item.rb +0 -3
- data/test/models/man.rb +0 -9
- data/test/models/matey.rb +0 -4
- data/test/models/member.rb +0 -12
- data/test/models/member_detail.rb +0 -5
- data/test/models/member_type.rb +0 -3
- data/test/models/membership.rb +0 -9
- data/test/models/minimalistic.rb +0 -2
- data/test/models/mixed_case_monkey.rb +0 -3
- data/test/models/movie.rb +0 -5
- data/test/models/order.rb +0 -4
- data/test/models/organization.rb +0 -6
- data/test/models/owner.rb +0 -5
- data/test/models/parrot.rb +0 -22
- data/test/models/person.rb +0 -16
- data/test/models/pet.rb +0 -5
- data/test/models/pirate.rb +0 -80
- data/test/models/polymorphic_design.rb +0 -3
- data/test/models/polymorphic_price.rb +0 -3
- data/test/models/post.rb +0 -102
- data/test/models/price_estimate.rb +0 -3
- data/test/models/project.rb +0 -30
- data/test/models/reader.rb +0 -4
- data/test/models/reference.rb +0 -4
- data/test/models/reply.rb +0 -46
- data/test/models/ship.rb +0 -19
- data/test/models/ship_part.rb +0 -7
- data/test/models/sponsor.rb +0 -4
- data/test/models/subject.rb +0 -4
- data/test/models/subscriber.rb +0 -8
- data/test/models/subscription.rb +0 -4
- data/test/models/tag.rb +0 -7
- data/test/models/tagging.rb +0 -10
- data/test/models/task.rb +0 -3
- data/test/models/tee.rb +0 -4
- data/test/models/tie.rb +0 -4
- data/test/models/topic.rb +0 -80
- data/test/models/toy.rb +0 -6
- data/test/models/treasure.rb +0 -8
- data/test/models/vertex.rb +0 -9
- data/test/models/warehouse_thing.rb +0 -5
- data/test/models/zine.rb +0 -3
- data/test/schema/mysql_specific_schema.rb +0 -31
- data/test/schema/postgresql_specific_schema.rb +0 -114
- data/test/schema/schema.rb +0 -550
- data/test/schema/schema2.rb +0 -6
- data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -1,193 +0,0 @@
|
|
1
|
-
require "cases/helper"
|
2
|
-
require 'models/topic'
|
3
|
-
require 'models/developer'
|
4
|
-
require 'models/reply'
|
5
|
-
require 'models/minimalistic'
|
6
|
-
|
7
|
-
class Topic; def after_find() end end
|
8
|
-
class Developer; def after_find() end end
|
9
|
-
class SpecialDeveloper < Developer; end
|
10
|
-
|
11
|
-
class TopicManualObserver
|
12
|
-
include Singleton
|
13
|
-
|
14
|
-
attr_reader :action, :object, :callbacks
|
15
|
-
|
16
|
-
def initialize
|
17
|
-
Topic.add_observer(self)
|
18
|
-
@callbacks = []
|
19
|
-
end
|
20
|
-
|
21
|
-
def update(callback_method, object)
|
22
|
-
@callbacks << { "callback_method" => callback_method, "object" => object }
|
23
|
-
end
|
24
|
-
|
25
|
-
def has_been_notified?
|
26
|
-
!@callbacks.empty?
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
class TopicaAuditor < ActiveRecord::Observer
|
31
|
-
observe :topic
|
32
|
-
|
33
|
-
attr_reader :topic
|
34
|
-
|
35
|
-
def after_find(topic)
|
36
|
-
@topic = topic
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class TopicObserver < ActiveRecord::Observer
|
41
|
-
attr_reader :topic
|
42
|
-
|
43
|
-
def after_find(topic)
|
44
|
-
@topic = topic
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
class MinimalisticObserver < ActiveRecord::Observer
|
49
|
-
attr_reader :minimalistic
|
50
|
-
|
51
|
-
def after_find(minimalistic)
|
52
|
-
@minimalistic = minimalistic
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
class MultiObserver < ActiveRecord::Observer
|
57
|
-
attr_reader :record
|
58
|
-
|
59
|
-
def self.observed_class() [ Topic, Developer ] end
|
60
|
-
|
61
|
-
cattr_reader :last_inherited
|
62
|
-
@@last_inherited = nil
|
63
|
-
|
64
|
-
def observed_class_inherited_with_testing(subclass)
|
65
|
-
observed_class_inherited_without_testing(subclass)
|
66
|
-
@@last_inherited = subclass
|
67
|
-
end
|
68
|
-
|
69
|
-
alias_method_chain :observed_class_inherited, :testing
|
70
|
-
|
71
|
-
def after_find(record)
|
72
|
-
@record = record
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
class LifecycleTest < ActiveRecord::TestCase
|
77
|
-
fixtures :topics, :developers, :minimalistics
|
78
|
-
|
79
|
-
def test_before_destroy
|
80
|
-
original_count = Topic.count
|
81
|
-
(topic_to_be_destroyed = Topic.find(1)).destroy
|
82
|
-
assert_equal original_count - (1 + topic_to_be_destroyed.replies.size), Topic.count
|
83
|
-
end
|
84
|
-
|
85
|
-
def test_after_save
|
86
|
-
ActiveRecord::Base.observers = :topic_manual_observer
|
87
|
-
ActiveRecord::Base.instantiate_observers
|
88
|
-
|
89
|
-
topic = Topic.find(1)
|
90
|
-
topic.title = "hello"
|
91
|
-
topic.save
|
92
|
-
|
93
|
-
assert TopicManualObserver.instance.has_been_notified?
|
94
|
-
assert_equal :after_save, TopicManualObserver.instance.callbacks.last["callback_method"]
|
95
|
-
end
|
96
|
-
|
97
|
-
def test_observer_update_on_save
|
98
|
-
ActiveRecord::Base.observers = TopicManualObserver
|
99
|
-
ActiveRecord::Base.instantiate_observers
|
100
|
-
|
101
|
-
topic = Topic.find(1)
|
102
|
-
assert TopicManualObserver.instance.has_been_notified?
|
103
|
-
assert_equal :after_find, TopicManualObserver.instance.callbacks.first["callback_method"]
|
104
|
-
end
|
105
|
-
|
106
|
-
def test_auto_observer
|
107
|
-
topic_observer = TopicaAuditor.instance
|
108
|
-
assert_nil TopicaAuditor.observed_class
|
109
|
-
assert_equal [Topic], TopicaAuditor.instance.observed_classes.to_a
|
110
|
-
|
111
|
-
topic = Topic.find(1)
|
112
|
-
assert_equal topic.title, topic_observer.topic.title
|
113
|
-
end
|
114
|
-
|
115
|
-
def test_inferred_auto_observer
|
116
|
-
topic_observer = TopicObserver.instance
|
117
|
-
assert_equal Topic, TopicObserver.observed_class
|
118
|
-
|
119
|
-
topic = Topic.find(1)
|
120
|
-
assert_equal topic.title, topic_observer.topic.title
|
121
|
-
end
|
122
|
-
|
123
|
-
def test_observing_two_classes
|
124
|
-
multi_observer = MultiObserver.instance
|
125
|
-
|
126
|
-
topic = Topic.find(1)
|
127
|
-
assert_equal topic.title, multi_observer.record.title
|
128
|
-
|
129
|
-
developer = Developer.find(1)
|
130
|
-
assert_equal developer.name, multi_observer.record.name
|
131
|
-
end
|
132
|
-
|
133
|
-
def test_observing_subclasses
|
134
|
-
multi_observer = MultiObserver.instance
|
135
|
-
|
136
|
-
developer = SpecialDeveloper.find(1)
|
137
|
-
assert_equal developer.name, multi_observer.record.name
|
138
|
-
|
139
|
-
klass = Class.new(Developer)
|
140
|
-
assert_equal klass, multi_observer.last_inherited
|
141
|
-
|
142
|
-
developer = klass.find(1)
|
143
|
-
assert_equal developer.name, multi_observer.record.name
|
144
|
-
end
|
145
|
-
|
146
|
-
def test_after_find_can_be_observed_when_its_not_defined_on_the_model
|
147
|
-
observer = MinimalisticObserver.instance
|
148
|
-
assert_equal Minimalistic, MinimalisticObserver.observed_class
|
149
|
-
|
150
|
-
minimalistic = Minimalistic.find(1)
|
151
|
-
assert_equal minimalistic, observer.minimalistic
|
152
|
-
end
|
153
|
-
|
154
|
-
def test_after_find_can_be_observed_when_its_defined_on_the_model
|
155
|
-
observer = TopicObserver.instance
|
156
|
-
assert_equal Topic, TopicObserver.observed_class
|
157
|
-
|
158
|
-
topic = Topic.find(1)
|
159
|
-
assert_equal topic, observer.topic
|
160
|
-
end
|
161
|
-
|
162
|
-
def test_after_find_is_not_created_if_its_not_used
|
163
|
-
# use a fresh class so an observer can't have defined an
|
164
|
-
# after_find on it
|
165
|
-
model_class = Class.new(ActiveRecord::Base)
|
166
|
-
observer_class = Class.new(ActiveRecord::Observer)
|
167
|
-
observer_class.observe(model_class)
|
168
|
-
|
169
|
-
observer = observer_class.instance
|
170
|
-
|
171
|
-
assert !model_class.method_defined?(:after_find)
|
172
|
-
end
|
173
|
-
|
174
|
-
def test_after_find_is_not_clobbered_if_it_already_exists
|
175
|
-
# use a fresh observer class so we can instantiate it (Observer is
|
176
|
-
# a Singleton)
|
177
|
-
model_class = Class.new(ActiveRecord::Base) do
|
178
|
-
def after_find; end
|
179
|
-
end
|
180
|
-
original_method = model_class.instance_method(:after_find)
|
181
|
-
observer_class = Class.new(ActiveRecord::Observer) do
|
182
|
-
def after_find; end
|
183
|
-
end
|
184
|
-
observer_class.observe(model_class)
|
185
|
-
|
186
|
-
observer = observer_class.instance
|
187
|
-
assert_equal original_method, model_class.instance_method(:after_find)
|
188
|
-
end
|
189
|
-
|
190
|
-
def test_invalid_observer
|
191
|
-
assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
|
192
|
-
end
|
193
|
-
end
|
data/test/cases/locking_test.rb
DELETED
@@ -1,350 +0,0 @@
|
|
1
|
-
require "cases/helper"
|
2
|
-
require 'models/person'
|
3
|
-
require 'models/reader'
|
4
|
-
require 'models/legacy_thing'
|
5
|
-
require 'models/reference'
|
6
|
-
|
7
|
-
class LockWithoutDefault < ActiveRecord::Base; end
|
8
|
-
|
9
|
-
class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
|
10
|
-
set_table_name :lock_without_defaults_cust
|
11
|
-
set_locking_column :custom_lock_version
|
12
|
-
end
|
13
|
-
|
14
|
-
class ReadonlyFirstNamePerson < Person
|
15
|
-
attr_readonly :first_name
|
16
|
-
end
|
17
|
-
|
18
|
-
class OptimisticLockingTest < ActiveRecord::TestCase
|
19
|
-
fixtures :people, :legacy_things, :references
|
20
|
-
|
21
|
-
# need to disable transactional fixtures, because otherwise the sqlite3
|
22
|
-
# adapter (at least) chokes when we try and change the schema in the middle
|
23
|
-
# of a test (see test_increment_counter_*).
|
24
|
-
self.use_transactional_fixtures = false
|
25
|
-
|
26
|
-
def test_lock_existing
|
27
|
-
p1 = Person.find(1)
|
28
|
-
p2 = Person.find(1)
|
29
|
-
assert_equal 0, p1.lock_version
|
30
|
-
assert_equal 0, p2.lock_version
|
31
|
-
|
32
|
-
p1.first_name = 'stu'
|
33
|
-
p1.save!
|
34
|
-
assert_equal 1, p1.lock_version
|
35
|
-
assert_equal 0, p2.lock_version
|
36
|
-
|
37
|
-
p2.first_name = 'sue'
|
38
|
-
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
39
|
-
end
|
40
|
-
|
41
|
-
# See Lighthouse ticket #1966
|
42
|
-
def test_lock_destroy
|
43
|
-
p1 = Person.find(1)
|
44
|
-
p2 = Person.find(1)
|
45
|
-
assert_equal 0, p1.lock_version
|
46
|
-
assert_equal 0, p2.lock_version
|
47
|
-
|
48
|
-
p1.first_name = 'stu'
|
49
|
-
p1.save!
|
50
|
-
assert_equal 1, p1.lock_version
|
51
|
-
assert_equal 0, p2.lock_version
|
52
|
-
|
53
|
-
assert_raises(ActiveRecord::StaleObjectError) { p2.destroy }
|
54
|
-
|
55
|
-
assert p1.destroy
|
56
|
-
assert p1.frozen?
|
57
|
-
assert p1.destroyed?
|
58
|
-
assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) }
|
59
|
-
end
|
60
|
-
|
61
|
-
def test_lock_repeating
|
62
|
-
p1 = Person.find(1)
|
63
|
-
p2 = Person.find(1)
|
64
|
-
assert_equal 0, p1.lock_version
|
65
|
-
assert_equal 0, p2.lock_version
|
66
|
-
|
67
|
-
p1.first_name = 'stu'
|
68
|
-
p1.save!
|
69
|
-
assert_equal 1, p1.lock_version
|
70
|
-
assert_equal 0, p2.lock_version
|
71
|
-
|
72
|
-
p2.first_name = 'sue'
|
73
|
-
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
74
|
-
p2.first_name = 'sue2'
|
75
|
-
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
76
|
-
end
|
77
|
-
|
78
|
-
def test_lock_new
|
79
|
-
p1 = Person.new(:first_name => 'anika')
|
80
|
-
assert_equal 0, p1.lock_version
|
81
|
-
|
82
|
-
p1.first_name = 'anika2'
|
83
|
-
p1.save!
|
84
|
-
p2 = Person.find(p1.id)
|
85
|
-
assert_equal 0, p1.lock_version
|
86
|
-
assert_equal 0, p2.lock_version
|
87
|
-
|
88
|
-
p1.first_name = 'anika3'
|
89
|
-
p1.save!
|
90
|
-
assert_equal 1, p1.lock_version
|
91
|
-
assert_equal 0, p2.lock_version
|
92
|
-
|
93
|
-
p2.first_name = 'sue'
|
94
|
-
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
95
|
-
end
|
96
|
-
|
97
|
-
def test_lock_new_with_nil
|
98
|
-
p1 = Person.new(:first_name => 'anika')
|
99
|
-
p1.save!
|
100
|
-
p1.lock_version = nil # simulate bad fixture or column with no default
|
101
|
-
p1.save!
|
102
|
-
assert_equal 1, p1.lock_version
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
|
-
def test_lock_column_name_existing
|
107
|
-
t1 = LegacyThing.find(1)
|
108
|
-
t2 = LegacyThing.find(1)
|
109
|
-
assert_equal 0, t1.version
|
110
|
-
assert_equal 0, t2.version
|
111
|
-
|
112
|
-
t1.tps_report_number = 700
|
113
|
-
t1.save!
|
114
|
-
assert_equal 1, t1.version
|
115
|
-
assert_equal 0, t2.version
|
116
|
-
|
117
|
-
t2.tps_report_number = 800
|
118
|
-
assert_raise(ActiveRecord::StaleObjectError) { t2.save! }
|
119
|
-
end
|
120
|
-
|
121
|
-
def test_lock_column_is_mass_assignable
|
122
|
-
p1 = Person.create(:first_name => 'bianca')
|
123
|
-
assert_equal 0, p1.lock_version
|
124
|
-
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
|
125
|
-
|
126
|
-
p1.first_name = 'bianca2'
|
127
|
-
p1.save!
|
128
|
-
assert_equal 1, p1.lock_version
|
129
|
-
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
|
130
|
-
end
|
131
|
-
|
132
|
-
def test_lock_without_default_sets_version_to_zero
|
133
|
-
t1 = LockWithoutDefault.new
|
134
|
-
assert_equal 0, t1.lock_version
|
135
|
-
end
|
136
|
-
|
137
|
-
def test_lock_with_custom_column_without_default_sets_version_to_zero
|
138
|
-
t1 = LockWithCustomColumnWithoutDefault.new
|
139
|
-
assert_equal 0, t1.custom_lock_version
|
140
|
-
end
|
141
|
-
|
142
|
-
def test_readonly_attributes
|
143
|
-
assert_equal Set.new([ 'first_name' ]), ReadonlyFirstNamePerson.readonly_attributes
|
144
|
-
|
145
|
-
p = ReadonlyFirstNamePerson.create(:first_name => "unchangeable name")
|
146
|
-
p.reload
|
147
|
-
assert_equal "unchangeable name", p.first_name
|
148
|
-
|
149
|
-
p.update_attributes(:first_name => "changed name")
|
150
|
-
p.reload
|
151
|
-
assert_equal "unchangeable name", p.first_name
|
152
|
-
end
|
153
|
-
|
154
|
-
{ :lock_version => Person, :custom_lock_version => LegacyThing }.each do |name, model|
|
155
|
-
define_method("test_increment_counter_updates_#{name}") do
|
156
|
-
counter_test model, 1 do |id|
|
157
|
-
model.increment_counter :test_count, id
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
define_method("test_decrement_counter_updates_#{name}") do
|
162
|
-
counter_test model, -1 do |id|
|
163
|
-
model.decrement_counter :test_count, id
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
define_method("test_update_counters_updates_#{name}") do
|
168
|
-
counter_test model, 1 do |id|
|
169
|
-
model.update_counters id, :test_count => 1
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
# See Lighthouse ticket #1966
|
175
|
-
def test_destroy_dependents
|
176
|
-
# Establish dependent relationship between People and LegacyThing
|
177
|
-
add_counter_column_to(Person, 'legacy_things_count')
|
178
|
-
LegacyThing.connection.add_column LegacyThing.table_name, 'person_id', :integer
|
179
|
-
LegacyThing.reset_column_information
|
180
|
-
LegacyThing.class_eval do
|
181
|
-
belongs_to :person, :counter_cache => true
|
182
|
-
end
|
183
|
-
Person.class_eval do
|
184
|
-
has_many :legacy_things, :dependent => :destroy
|
185
|
-
end
|
186
|
-
|
187
|
-
# Make sure that counter incrementing doesn't cause problems
|
188
|
-
p1 = Person.new(:first_name => 'fjord')
|
189
|
-
p1.save!
|
190
|
-
t = LegacyThing.new(:person => p1)
|
191
|
-
t.save!
|
192
|
-
p1.reload
|
193
|
-
assert_equal 1, p1.legacy_things_count
|
194
|
-
assert p1.destroy
|
195
|
-
assert_equal true, p1.frozen?
|
196
|
-
assert_raises(ActiveRecord::RecordNotFound) { Person.find(p1.id) }
|
197
|
-
assert_raises(ActiveRecord::RecordNotFound) { LegacyThing.find(t.id) }
|
198
|
-
end
|
199
|
-
|
200
|
-
def test_quote_table_name
|
201
|
-
ref = references(:michael_magician)
|
202
|
-
ref.favourite = !ref.favourite
|
203
|
-
assert ref.save
|
204
|
-
end
|
205
|
-
|
206
|
-
# Useful for partial updates, don't only update the lock_version if there
|
207
|
-
# is nothing else being updated.
|
208
|
-
def test_update_without_attributes_does_not_only_update_lock_version
|
209
|
-
assert_nothing_raised do
|
210
|
-
p1 = Person.new(:first_name => 'anika')
|
211
|
-
p1.send(:update_with_lock, [])
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
private
|
216
|
-
|
217
|
-
def add_counter_column_to(model, col='test_count')
|
218
|
-
model.connection.add_column model.table_name, col, :integer, :null => false, :default => 0
|
219
|
-
model.reset_column_information
|
220
|
-
# OpenBase does not set a value to existing rows when adding a not null default column
|
221
|
-
model.update_all(col => 0) if current_adapter?(:OpenBaseAdapter)
|
222
|
-
end
|
223
|
-
|
224
|
-
def remove_counter_column_from(model)
|
225
|
-
model.connection.remove_column model.table_name, :test_count
|
226
|
-
model.reset_column_information
|
227
|
-
end
|
228
|
-
|
229
|
-
def counter_test(model, expected_count)
|
230
|
-
add_counter_column_to(model)
|
231
|
-
object = model.find(:first)
|
232
|
-
assert_equal 0, object.test_count
|
233
|
-
assert_equal 0, object.send(model.locking_column)
|
234
|
-
yield object.id
|
235
|
-
object.reload
|
236
|
-
assert_equal expected_count, object.test_count
|
237
|
-
assert_equal 1, object.send(model.locking_column)
|
238
|
-
ensure
|
239
|
-
remove_counter_column_from(model)
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
|
244
|
-
# TODO: test against the generated SQL since testing locking behavior itself
|
245
|
-
# is so cumbersome. Will deadlock Ruby threads if the underlying db.execute
|
246
|
-
# blocks, so separate script called by Kernel#system is needed.
|
247
|
-
# (See exec vs. async_exec in the PostgreSQL adapter.)
|
248
|
-
|
249
|
-
# TODO: The Sybase, and OpenBase adapters currently have no support for pessimistic locking
|
250
|
-
|
251
|
-
unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
|
252
|
-
class PessimisticLockingTest < ActiveRecord::TestCase
|
253
|
-
self.use_transactional_fixtures = false
|
254
|
-
fixtures :people, :readers
|
255
|
-
|
256
|
-
def setup
|
257
|
-
# Avoid introspection queries during tests.
|
258
|
-
Person.columns; Reader.columns
|
259
|
-
end
|
260
|
-
|
261
|
-
# Test typical find.
|
262
|
-
def test_sane_find_with_lock
|
263
|
-
assert_nothing_raised do
|
264
|
-
Person.transaction do
|
265
|
-
Person.find 1, :lock => true
|
266
|
-
end
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
# Test scoped lock.
|
271
|
-
def test_sane_find_with_scoped_lock
|
272
|
-
assert_nothing_raised do
|
273
|
-
Person.transaction do
|
274
|
-
Person.with_scope(:find => { :lock => true }) do
|
275
|
-
Person.find 1
|
276
|
-
end
|
277
|
-
end
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
# PostgreSQL protests SELECT ... FOR UPDATE on an outer join.
|
282
|
-
unless current_adapter?(:PostgreSQLAdapter)
|
283
|
-
# Test locked eager find.
|
284
|
-
def test_eager_find_with_lock
|
285
|
-
assert_nothing_raised do
|
286
|
-
Person.transaction do
|
287
|
-
Person.find 1, :include => :readers, :lock => true
|
288
|
-
end
|
289
|
-
end
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
# Locking a record reloads it.
|
294
|
-
def test_sane_lock_method
|
295
|
-
assert_nothing_raised do
|
296
|
-
Person.transaction do
|
297
|
-
person = Person.find 1
|
298
|
-
old, person.first_name = person.first_name, 'fooman'
|
299
|
-
person.lock!
|
300
|
-
assert_equal old, person.first_name
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
|
306
|
-
use_concurrent_connections
|
307
|
-
|
308
|
-
def test_no_locks_no_wait
|
309
|
-
first, second = duel { Person.find 1 }
|
310
|
-
assert first.end > second.end
|
311
|
-
end
|
312
|
-
|
313
|
-
def test_second_lock_waits
|
314
|
-
assert [0.2, 1, 5].any? { |zzz|
|
315
|
-
first, second = duel(zzz) { Person.find 1, :lock => true }
|
316
|
-
second.end > first.end
|
317
|
-
}
|
318
|
-
end
|
319
|
-
|
320
|
-
protected
|
321
|
-
def duel(zzz = 5)
|
322
|
-
t0, t1, t2, t3 = nil, nil, nil, nil
|
323
|
-
|
324
|
-
a = Thread.new do
|
325
|
-
t0 = Time.now
|
326
|
-
Person.transaction do
|
327
|
-
yield
|
328
|
-
sleep zzz # block thread 2 for zzz seconds
|
329
|
-
end
|
330
|
-
t1 = Time.now
|
331
|
-
end
|
332
|
-
|
333
|
-
b = Thread.new do
|
334
|
-
sleep zzz / 2.0 # ensure thread 1 tx starts first
|
335
|
-
t2 = Time.now
|
336
|
-
Person.transaction { yield }
|
337
|
-
t3 = Time.now
|
338
|
-
end
|
339
|
-
|
340
|
-
a.join
|
341
|
-
b.join
|
342
|
-
|
343
|
-
assert t1 > t0 + zzz
|
344
|
-
assert t2 > t0
|
345
|
-
assert t3 > t2
|
346
|
-
[t0.to_f..t1.to_f, t2.to_f..t3.to_f]
|
347
|
-
end
|
348
|
-
end
|
349
|
-
end
|
350
|
-
end
|