activerecord 2.3.18 → 3.2.22
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1014 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +222 -0
- data/examples/performance.rb +100 -126
- data/examples/simple.rb +14 -0
- data/lib/active_record/aggregations.rb +93 -99
- data/lib/active_record/associations/alias_tracker.rb +76 -0
- data/lib/active_record/associations/association.rb +247 -0
- data/lib/active_record/associations/association_scope.rb +134 -0
- data/lib/active_record/associations/belongs_to_association.rb +54 -61
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +17 -59
- data/lib/active_record/associations/builder/association.rb +55 -0
- data/lib/active_record/associations/builder/belongs_to.rb +88 -0
- data/lib/active_record/associations/builder/collection_association.rb +75 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -0
- data/lib/active_record/associations/builder/has_many.rb +71 -0
- data/lib/active_record/associations/builder/has_one.rb +62 -0
- data/lib/active_record/associations/builder/singular_association.rb +32 -0
- data/lib/active_record/associations/collection_association.rb +580 -0
- data/lib/active_record/associations/collection_proxy.rb +133 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +39 -119
- data/lib/active_record/associations/has_many_association.rb +60 -79
- data/lib/active_record/associations/has_many_through_association.rb +127 -206
- data/lib/active_record/associations/has_one_association.rb +55 -114
- data/lib/active_record/associations/has_one_through_association.rb +25 -26
- data/lib/active_record/associations/join_dependency/join_association.rb +159 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
- data/lib/active_record/associations/join_dependency.rb +214 -0
- data/lib/active_record/associations/join_helper.rb +55 -0
- data/lib/active_record/associations/preloader/association.rb +125 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +67 -0
- data/lib/active_record/associations/preloader.rb +181 -0
- data/lib/active_record/associations/singular_association.rb +64 -0
- data/lib/active_record/associations/through_association.rb +87 -0
- data/lib/active_record/associations.rb +693 -1337
- data/lib/active_record/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +31 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
- data/lib/active_record/attribute_methods/dirty.rb +111 -0
- data/lib/active_record/attribute_methods/primary_key.rb +114 -0
- data/lib/active_record/attribute_methods/query.rb +39 -0
- data/lib/active_record/attribute_methods/read.rb +136 -0
- data/lib/active_record/attribute_methods/serialization.rb +120 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
- data/lib/active_record/attribute_methods/write.rb +70 -0
- data/lib/active_record/attribute_methods.rb +211 -339
- data/lib/active_record/autosave_association.rb +179 -149
- data/lib/active_record/base.rb +401 -2907
- data/lib/active_record/callbacks.rb +91 -176
- data/lib/active_record/coders/yaml_column.rb +41 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +236 -119
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +110 -58
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +175 -74
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +71 -21
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +81 -311
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +194 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +130 -83
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +676 -0
- data/lib/active_record/connection_adapters/column.rb +296 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +280 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +272 -493
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +650 -405
- data/lib/active_record/connection_adapters/schema_cache.rb +69 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +30 -9
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +276 -147
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/counter_cache.rb +123 -0
- data/lib/active_record/dynamic_finder_match.rb +41 -14
- data/lib/active_record/dynamic_matchers.rb +84 -0
- data/lib/active_record/dynamic_scope_match.rb +13 -15
- data/lib/active_record/errors.rb +195 -0
- data/lib/active_record/explain.rb +86 -0
- data/lib/active_record/explain_subscriber.rb +25 -0
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/fixtures.rb +695 -770
- data/lib/active_record/identity_map.rb +162 -0
- data/lib/active_record/inheritance.rb +174 -0
- data/lib/active_record/integration.rb +60 -0
- data/lib/active_record/locale/en.yml +9 -27
- data/lib/active_record/locking/optimistic.rb +76 -73
- data/lib/active_record/locking/pessimistic.rb +32 -10
- data/lib/active_record/log_subscriber.rb +72 -0
- data/lib/active_record/migration/command_recorder.rb +105 -0
- data/lib/active_record/migration.rb +415 -205
- data/lib/active_record/model_schema.rb +368 -0
- data/lib/active_record/nested_attributes.rb +153 -63
- data/lib/active_record/observer.rb +27 -103
- data/lib/active_record/persistence.rb +376 -0
- data/lib/active_record/query_cache.rb +49 -8
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +131 -0
- data/lib/active_record/railties/console_sandbox.rb +6 -0
- data/lib/active_record/railties/controller_runtime.rb +49 -0
- data/lib/active_record/railties/databases.rake +659 -0
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +269 -120
- data/lib/active_record/relation/batches.rb +90 -0
- data/lib/active_record/relation/calculations.rb +372 -0
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +402 -0
- data/lib/active_record/relation/predicate_builder.rb +63 -0
- data/lib/active_record/relation/query_methods.rb +417 -0
- data/lib/active_record/relation/spawn_methods.rb +180 -0
- data/lib/active_record/relation.rb +537 -0
- data/lib/active_record/result.rb +40 -0
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema.rb +9 -6
- data/lib/active_record/schema_dumper.rb +55 -32
- data/lib/active_record/scoping/default.rb +142 -0
- data/lib/active_record/scoping/named.rb +200 -0
- data/lib/active_record/scoping.rb +152 -0
- data/lib/active_record/serialization.rb +8 -91
- data/lib/active_record/serializers/xml_serializer.rb +43 -197
- data/lib/active_record/session_store.rb +129 -103
- data/lib/active_record/store.rb +52 -0
- data/lib/active_record/test_case.rb +30 -23
- data/lib/active_record/timestamp.rb +95 -52
- data/lib/active_record/transactions.rb +212 -66
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +43 -0
- data/lib/active_record/validations/uniqueness.rb +180 -0
- data/lib/active_record/validations.rb +43 -1106
- data/lib/active_record/version.rb +5 -4
- data/lib/active_record.rb +121 -48
- data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +34 -0
- data/lib/rails/generators/active_record/migration.rb +15 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +47 -0
- data/lib/rails/generators/active_record/model/templates/migration.rb +15 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +12 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
- data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
- data/lib/rails/generators/active_record/observer/templates/observer.rb +4 -0
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +25 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +12 -0
- data/lib/rails/generators/active_record.rb +25 -0
- metadata +187 -363
- data/CHANGELOG +0 -5904
- data/README +0 -351
- data/RUNNING_UNIT_TESTS +0 -36
- data/Rakefile +0 -268
- data/install.rb +0 -30
- data/lib/active_record/association_preload.rb +0 -406
- data/lib/active_record/associations/association_collection.rb +0 -533
- data/lib/active_record/associations/association_proxy.rb +0 -288
- data/lib/active_record/batches.rb +0 -85
- data/lib/active_record/calculations.rb +0 -321
- data/lib/active_record/dirty.rb +0 -183
- data/lib/active_record/named_scope.rb +0 -197
- 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/special_categories.yml +0 -9
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
- data/test/fixtures/categories.yml +0 -14
- 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,15 +1,22 @@
|
|
1
|
+
require 'active_support/core_ext/hash/except'
|
2
|
+
require 'active_support/core_ext/object/try'
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
5
|
+
require 'active_support/core_ext/class/attribute'
|
6
|
+
|
1
7
|
module ActiveRecord
|
2
8
|
module NestedAttributes #:nodoc:
|
3
9
|
class TooManyRecords < ActiveRecordError
|
4
10
|
end
|
5
11
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
included do
|
15
|
+
class_attribute :nested_attributes_options, :instance_writer => false
|
16
|
+
self.nested_attributes_options = {}
|
10
17
|
end
|
11
18
|
|
12
|
-
#
|
19
|
+
# = Active Record Nested Attributes
|
13
20
|
#
|
14
21
|
# Nested attributes allow you to save attributes on associated records
|
15
22
|
# through the parent. By default nested attribute updating is turned off,
|
@@ -19,6 +26,7 @@ module ActiveRecord
|
|
19
26
|
#
|
20
27
|
# The attribute writer is named after the association, which means that
|
21
28
|
# in the following example, two new methods are added to your model:
|
29
|
+
#
|
22
30
|
# <tt>author_attributes=(attributes)</tt> and
|
23
31
|
# <tt>pages_attributes=(attributes)</tt>.
|
24
32
|
#
|
@@ -45,14 +53,14 @@ module ActiveRecord
|
|
45
53
|
# create the member and avatar in one go:
|
46
54
|
#
|
47
55
|
# params = { :member => { :name => 'Jack', :avatar_attributes => { :icon => 'smiling' } } }
|
48
|
-
# member = Member.create(params)
|
56
|
+
# member = Member.create(params[:member])
|
49
57
|
# member.avatar.id # => 2
|
50
58
|
# member.avatar.icon # => 'smiling'
|
51
59
|
#
|
52
60
|
# It also allows you to update the avatar through the member:
|
53
61
|
#
|
54
|
-
# params = { :member
|
55
|
-
# member.update_attributes params[
|
62
|
+
# params = { :member => { :avatar_attributes => { :id => '2', :icon => 'sad' } } }
|
63
|
+
# member.update_attributes params[:member]
|
56
64
|
# member.avatar.icon # => 'sad'
|
57
65
|
#
|
58
66
|
# By default you will only be able to set and update attributes on the
|
@@ -71,7 +79,7 @@ module ActiveRecord
|
|
71
79
|
# member.avatar_attributes = { :id => '2', :_destroy => '1' }
|
72
80
|
# member.avatar.marked_for_destruction? # => true
|
73
81
|
# member.save
|
74
|
-
# member.avatar
|
82
|
+
# member.reload.avatar # => nil
|
75
83
|
#
|
76
84
|
# Note that the model will _not_ be destroyed until the parent is saved.
|
77
85
|
#
|
@@ -84,8 +92,9 @@ module ActiveRecord
|
|
84
92
|
# accepts_nested_attributes_for :posts
|
85
93
|
# end
|
86
94
|
#
|
87
|
-
# You can now set or update attributes on
|
88
|
-
#
|
95
|
+
# You can now set or update attributes on the associated posts through
|
96
|
+
# an attribute hash for a member: include the key +:posts_attributes+
|
97
|
+
# with an array of hashes of post attributes as a value.
|
89
98
|
#
|
90
99
|
# For each hash that does _not_ have an <tt>id</tt> key a new record will
|
91
100
|
# be instantiated, unless the hash also contains a <tt>_destroy</tt> key
|
@@ -108,10 +117,10 @@ module ActiveRecord
|
|
108
117
|
# hashes if they fail to pass your criteria. For example, the previous
|
109
118
|
# example could be rewritten as:
|
110
119
|
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
120
|
+
# class Member < ActiveRecord::Base
|
121
|
+
# has_many :posts
|
122
|
+
# accepts_nested_attributes_for :posts, :reject_if => proc { |attributes| attributes['title'].blank? }
|
123
|
+
# end
|
115
124
|
#
|
116
125
|
# params = { :member => {
|
117
126
|
# :name => 'joe', :posts_attributes => [
|
@@ -126,21 +135,21 @@ module ActiveRecord
|
|
126
135
|
# member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
|
127
136
|
# member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
|
128
137
|
#
|
129
|
-
#
|
138
|
+
# Alternatively, :reject_if also accepts a symbol for using methods:
|
130
139
|
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
140
|
+
# class Member < ActiveRecord::Base
|
141
|
+
# has_many :posts
|
142
|
+
# accepts_nested_attributes_for :posts, :reject_if => :new_record?
|
143
|
+
# end
|
135
144
|
#
|
136
|
-
#
|
137
|
-
#
|
138
|
-
#
|
145
|
+
# class Member < ActiveRecord::Base
|
146
|
+
# has_many :posts
|
147
|
+
# accepts_nested_attributes_for :posts, :reject_if => :reject_posts
|
139
148
|
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
149
|
+
# def reject_posts(attributed)
|
150
|
+
# attributed['title'].blank?
|
151
|
+
# end
|
152
|
+
# end
|
144
153
|
#
|
145
154
|
# If the hash contains an <tt>id</tt> key that matches an already
|
146
155
|
# associated record, the matching record will be modified:
|
@@ -173,9 +182,32 @@ module ActiveRecord
|
|
173
182
|
#
|
174
183
|
# member.attributes = params['member']
|
175
184
|
# member.posts.detect { |p| p.id == 2 }.marked_for_destruction? # => true
|
176
|
-
# member.posts.length
|
185
|
+
# member.posts.length # => 2
|
177
186
|
# member.save
|
178
|
-
# member.posts.length # => 1
|
187
|
+
# member.reload.posts.length # => 1
|
188
|
+
#
|
189
|
+
# Nested attributes for an associated collection can also be passed in
|
190
|
+
# the form of a hash of hashes instead of an array of hashes:
|
191
|
+
#
|
192
|
+
# Member.create(:name => 'joe',
|
193
|
+
# :posts_attributes => { :first => { :title => 'Foo' },
|
194
|
+
# :second => { :title => 'Bar' } })
|
195
|
+
#
|
196
|
+
# has the same effect as
|
197
|
+
#
|
198
|
+
# Member.create(:name => 'joe',
|
199
|
+
# :posts_attributes => [ { :title => 'Foo' },
|
200
|
+
# { :title => 'Bar' } ])
|
201
|
+
#
|
202
|
+
# The keys of the hash which is the value for +:posts_attributes+ are
|
203
|
+
# ignored in this case.
|
204
|
+
# However, it is not allowed to use +'id'+ or +:id+ for one of
|
205
|
+
# such keys, otherwise the hash will be wrapped in an array and
|
206
|
+
# interpreted as an attribute hash for a single post.
|
207
|
+
#
|
208
|
+
# Passing attributes for an associated collection in the form of a hash
|
209
|
+
# of hashes can be used with hashes generated from HTTP/HTML parameters,
|
210
|
+
# where there maybe no natural way to submit an array of hashes.
|
179
211
|
#
|
180
212
|
# === Saving
|
181
213
|
#
|
@@ -183,8 +215,36 @@ module ActiveRecord
|
|
183
215
|
# destruction, are saved and destroyed automatically and atomically when
|
184
216
|
# the parent model is saved. This happens inside the transaction initiated
|
185
217
|
# by the parents save method. See ActiveRecord::AutosaveAssociation.
|
218
|
+
#
|
219
|
+
# === Using with attr_accessible
|
220
|
+
#
|
221
|
+
# The use of <tt>attr_accessible</tt> can interfere with nested attributes
|
222
|
+
# if you're not careful. For example, if the <tt>Member</tt> model above
|
223
|
+
# was using <tt>attr_accessible</tt> like this:
|
224
|
+
#
|
225
|
+
# attr_accessible :name
|
226
|
+
#
|
227
|
+
# You would need to modify it to look like this:
|
228
|
+
#
|
229
|
+
# attr_accessible :name, :posts_attributes
|
230
|
+
#
|
231
|
+
# === Validating the presence of a parent model
|
232
|
+
#
|
233
|
+
# If you want to validate that a child record is associated with a parent
|
234
|
+
# record, you can use <tt>validates_presence_of</tt> and
|
235
|
+
# <tt>inverse_of</tt> as this example illustrates:
|
236
|
+
#
|
237
|
+
# class Member < ActiveRecord::Base
|
238
|
+
# has_many :posts, :inverse_of => :member
|
239
|
+
# accepts_nested_attributes_for :posts
|
240
|
+
# end
|
241
|
+
#
|
242
|
+
# class Post < ActiveRecord::Base
|
243
|
+
# belongs_to :member, :inverse_of => :posts
|
244
|
+
# validates_presence_of :member
|
245
|
+
# end
|
186
246
|
module ClassMethods
|
187
|
-
REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |
|
247
|
+
REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }
|
188
248
|
|
189
249
|
# Defines an attributes writer for the specified association(s). If you
|
190
250
|
# are using <tt>attr_protected</tt> or <tt>attr_accessible</tt>, then you
|
@@ -203,10 +263,11 @@ module ActiveRecord
|
|
203
263
|
# is specified, a record will be built for all attribute hashes that
|
204
264
|
# do not have a <tt>_destroy</tt> value that evaluates to true.
|
205
265
|
# Passing <tt>:all_blank</tt> instead of a Proc will create a proc
|
206
|
-
# that will reject a record where all the attributes are blank
|
266
|
+
# that will reject a record where all the attributes are blank excluding
|
267
|
+
# any value for _destroy.
|
207
268
|
# [:limit]
|
208
269
|
# Allows you to specify the maximum number of the associated records that
|
209
|
-
# can be
|
270
|
+
# can be processed with the nested attributes. If the size of the
|
210
271
|
# nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
|
211
272
|
# exception is raised. If omitted, any number associations can be processed.
|
212
273
|
# Note that the :limit option is only applicable to one-to-many associations.
|
@@ -233,17 +294,25 @@ module ActiveRecord
|
|
233
294
|
if reflection = reflect_on_association(association_name)
|
234
295
|
reflection.options[:autosave] = true
|
235
296
|
add_autosave_association_callbacks(reflection)
|
297
|
+
|
298
|
+
nested_attributes_options = self.nested_attributes_options.dup
|
236
299
|
nested_attributes_options[association_name.to_sym] = options
|
300
|
+
self.nested_attributes_options = nested_attributes_options
|
301
|
+
|
237
302
|
type = (reflection.collection? ? :collection : :one_to_one)
|
238
303
|
|
304
|
+
# remove_possible_method :pirate_attributes=
|
305
|
+
#
|
239
306
|
# def pirate_attributes=(attributes)
|
240
|
-
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
|
307
|
+
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
|
241
308
|
# end
|
242
|
-
class_eval <<-
|
309
|
+
class_eval <<-eoruby, __FILE__, __LINE__ + 1
|
310
|
+
remove_possible_method(:#{association_name}_attributes=)
|
311
|
+
|
243
312
|
def #{association_name}_attributes=(attributes)
|
244
|
-
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
|
313
|
+
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options)
|
245
314
|
end
|
246
|
-
|
315
|
+
eoruby
|
247
316
|
else
|
248
317
|
raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
|
249
318
|
end
|
@@ -269,29 +338,28 @@ module ActiveRecord
|
|
269
338
|
# Assigns the given attributes to the association.
|
270
339
|
#
|
271
340
|
# If update_only is false and the given attributes include an <tt>:id</tt>
|
272
|
-
# that matches the existing record
|
341
|
+
# that matches the existing record's id, then the existing record will be
|
273
342
|
# modified. If update_only is true, a new record is only created when no
|
274
343
|
# object exists. Otherwise a new record will be built.
|
275
344
|
#
|
276
345
|
# If the given attributes include a matching <tt>:id</tt> attribute, or
|
277
346
|
# update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
|
278
347
|
# then the existing record will be marked for destruction.
|
279
|
-
def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
|
280
|
-
options = nested_attributes_options[association_name]
|
348
|
+
def assign_nested_attributes_for_one_to_one_association(association_name, attributes, assignment_opts = {})
|
349
|
+
options = self.nested_attributes_options[association_name]
|
281
350
|
attributes = attributes.with_indifferent_access
|
282
|
-
check_existing_record = (options[:update_only] || !attributes['id'].blank?)
|
283
351
|
|
284
|
-
if
|
352
|
+
if (options[:update_only] || !attributes['id'].blank?) && (record = send(association_name)) &&
|
285
353
|
(options[:update_only] || record.id.to_s == attributes['id'].to_s)
|
286
|
-
assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy])
|
354
|
+
assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy], assignment_opts) unless call_reject_if(association_name, attributes)
|
287
355
|
|
288
|
-
elsif attributes['id']
|
356
|
+
elsif attributes['id'].present? && !assignment_opts[:without_protection]
|
289
357
|
raise_nested_attributes_record_not_found(association_name, attributes['id'])
|
290
358
|
|
291
359
|
elsif !reject_new_record?(association_name, attributes)
|
292
360
|
method = "build_#{association_name}"
|
293
361
|
if respond_to?(method)
|
294
|
-
send(method, attributes.except(*
|
362
|
+
send(method, attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
|
295
363
|
else
|
296
364
|
raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
|
297
365
|
end
|
@@ -315,7 +383,7 @@ module ActiveRecord
|
|
315
383
|
# })
|
316
384
|
#
|
317
385
|
# Will update the name of the Person with ID 1, build a new associated
|
318
|
-
# person with the name `John', and mark the
|
386
|
+
# person with the name `John', and mark the associated Person with ID 2
|
319
387
|
# for destruction.
|
320
388
|
#
|
321
389
|
# Also accepts an Array of attribute hashes:
|
@@ -325,8 +393,8 @@ module ActiveRecord
|
|
325
393
|
# { :name => 'John' },
|
326
394
|
# { :id => '2', :_destroy => true }
|
327
395
|
# ])
|
328
|
-
def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
|
329
|
-
options = nested_attributes_options[association_name]
|
396
|
+
def assign_nested_attributes_for_collection_association(association_name, attributes_collection, assignment_opts = {})
|
397
|
+
options = self.nested_attributes_options[association_name]
|
330
398
|
|
331
399
|
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
|
332
400
|
raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
|
@@ -337,16 +405,21 @@ module ActiveRecord
|
|
337
405
|
end
|
338
406
|
|
339
407
|
if attributes_collection.is_a? Hash
|
340
|
-
|
408
|
+
keys = attributes_collection.keys
|
409
|
+
attributes_collection = if keys.include?('id') || keys.include?(:id)
|
410
|
+
Array.wrap(attributes_collection)
|
411
|
+
else
|
412
|
+
attributes_collection.values
|
413
|
+
end
|
341
414
|
end
|
342
415
|
|
343
|
-
association =
|
416
|
+
association = association(association_name)
|
344
417
|
|
345
418
|
existing_records = if association.loaded?
|
346
|
-
association.
|
419
|
+
association.target
|
347
420
|
else
|
348
421
|
attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
|
349
|
-
attribute_ids.
|
422
|
+
attribute_ids.empty? ? [] : association.scoped.where(association.klass.primary_key => attribute_ids)
|
350
423
|
end
|
351
424
|
|
352
425
|
attributes_collection.each do |attributes|
|
@@ -354,11 +427,27 @@ module ActiveRecord
|
|
354
427
|
|
355
428
|
if attributes['id'].blank?
|
356
429
|
unless reject_new_record?(association_name, attributes)
|
357
|
-
association.build(attributes.except(*
|
430
|
+
association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
|
358
431
|
end
|
359
432
|
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
|
360
|
-
association.
|
361
|
-
|
433
|
+
unless association.loaded? || call_reject_if(association_name, attributes)
|
434
|
+
# Make sure we are operating on the actual object which is in the association's
|
435
|
+
# proxy_target array (either by finding it, or adding it if not found)
|
436
|
+
target_record = association.target.detect { |record| record == existing_record }
|
437
|
+
|
438
|
+
if target_record
|
439
|
+
existing_record = target_record
|
440
|
+
else
|
441
|
+
association.add_to_target(existing_record)
|
442
|
+
end
|
443
|
+
|
444
|
+
end
|
445
|
+
|
446
|
+
if !call_reject_if(association_name, attributes)
|
447
|
+
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy], assignment_opts)
|
448
|
+
end
|
449
|
+
elsif assignment_opts[:without_protection]
|
450
|
+
association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
|
362
451
|
else
|
363
452
|
raise_nested_attributes_record_not_found(association_name, attributes['id'])
|
364
453
|
end
|
@@ -367,12 +456,9 @@ module ActiveRecord
|
|
367
456
|
|
368
457
|
# Updates a record with the +attributes+ or marks it for destruction if
|
369
458
|
# +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
|
370
|
-
def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
|
371
|
-
|
372
|
-
|
373
|
-
else
|
374
|
-
record.attributes = attributes.except(*UNASSIGNABLE_KEYS)
|
375
|
-
end
|
459
|
+
def assign_to_or_mark_for_destruction(record, attributes, allow_destroy, assignment_opts)
|
460
|
+
record.assign_attributes(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
|
461
|
+
record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
|
376
462
|
end
|
377
463
|
|
378
464
|
# Determines if a hash contains a truthy _destroy key.
|
@@ -388,7 +474,8 @@ module ActiveRecord
|
|
388
474
|
end
|
389
475
|
|
390
476
|
def call_reject_if(association_name, attributes)
|
391
|
-
|
477
|
+
return false if has_destroy_flag?(attributes)
|
478
|
+
case callback = self.nested_attributes_options[association_name][:reject_if]
|
392
479
|
when Symbol
|
393
480
|
method(callback).arity == 0 ? send(callback) : send(callback, attributes)
|
394
481
|
when Proc
|
@@ -397,8 +484,11 @@ module ActiveRecord
|
|
397
484
|
end
|
398
485
|
|
399
486
|
def raise_nested_attributes_record_not_found(association_name, record_id)
|
400
|
-
|
401
|
-
|
487
|
+
raise RecordNotFound, "Couldn't find #{self.class.reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
|
488
|
+
end
|
489
|
+
|
490
|
+
def unassignable_keys(assignment_opts)
|
491
|
+
assignment_opts[:without_protection] ? UNASSIGNABLE_KEYS - %w[id] : UNASSIGNABLE_KEYS
|
402
492
|
end
|
403
493
|
end
|
404
494
|
end
|
@@ -1,60 +1,9 @@
|
|
1
|
-
require '
|
2
|
-
require 'set'
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
3
2
|
|
4
3
|
module ActiveRecord
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
# Activates the observers assigned. Examples:
|
12
|
-
#
|
13
|
-
# # Calls PersonObserver.instance
|
14
|
-
# ActiveRecord::Base.observers = :person_observer
|
15
|
-
#
|
16
|
-
# # Calls Cacher.instance and GarbageCollector.instance
|
17
|
-
# ActiveRecord::Base.observers = :cacher, :garbage_collector
|
18
|
-
#
|
19
|
-
# # Same as above, just using explicit class references
|
20
|
-
# ActiveRecord::Base.observers = Cacher, GarbageCollector
|
21
|
-
#
|
22
|
-
# Note: Setting this does not instantiate the observers yet. +instantiate_observers+ is
|
23
|
-
# called during startup, and before each development request.
|
24
|
-
def observers=(*observers)
|
25
|
-
@observers = observers.flatten
|
26
|
-
end
|
27
|
-
|
28
|
-
# Gets the current observers.
|
29
|
-
def observers
|
30
|
-
@observers ||= []
|
31
|
-
end
|
32
|
-
|
33
|
-
# Instantiate the global Active Record observers.
|
34
|
-
def instantiate_observers
|
35
|
-
return if @observers.blank?
|
36
|
-
@observers.each do |observer|
|
37
|
-
if observer.respond_to?(:to_sym) # Symbol or String
|
38
|
-
observer.to_s.camelize.constantize.instance
|
39
|
-
elsif observer.respond_to?(:instance)
|
40
|
-
observer.instance
|
41
|
-
else
|
42
|
-
raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
protected
|
48
|
-
# Notify observers when the observed class is subclassed.
|
49
|
-
def inherited(subclass)
|
50
|
-
super
|
51
|
-
changed
|
52
|
-
notify_observers :observed_class_inherited, subclass
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# Observer classes respond to lifecycle callbacks to implement trigger-like
|
4
|
+
# = Active Record Observer
|
5
|
+
#
|
6
|
+
# Observer classes respond to life cycle callbacks to implement trigger-like
|
58
7
|
# behavior outside the original class. This is a great way to reduce the
|
59
8
|
# clutter that normally comes when the model class is burdened with
|
60
9
|
# functionality that doesn't pertain to the core responsibility of the
|
@@ -62,7 +11,7 @@ module ActiveRecord
|
|
62
11
|
#
|
63
12
|
# class CommentObserver < ActiveRecord::Observer
|
64
13
|
# def after_save(comment)
|
65
|
-
# Notifications.
|
14
|
+
# Notifications.comment("admin@do.com", "New comment was posted", comment).deliver
|
66
15
|
# end
|
67
16
|
# end
|
68
17
|
#
|
@@ -118,8 +67,8 @@ module ActiveRecord
|
|
118
67
|
#
|
119
68
|
# == Configuration
|
120
69
|
#
|
121
|
-
# In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration
|
122
|
-
# <tt>config/
|
70
|
+
# In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration
|
71
|
+
# setting in your <tt>config/application.rb</tt> file.
|
123
72
|
#
|
124
73
|
# config.active_record.observers = :comment_observer, :signup_observer
|
125
74
|
#
|
@@ -139,58 +88,33 @@ module ActiveRecord
|
|
139
88
|
# load their observers by calling <tt>ModelObserver.instance</tt> before. Observers are
|
140
89
|
# singletons and that call instantiates and registers them.
|
141
90
|
#
|
142
|
-
class Observer
|
143
|
-
include Singleton
|
144
|
-
|
145
|
-
class << self
|
146
|
-
# Attaches the observer to the supplied model classes.
|
147
|
-
def observe(*models)
|
148
|
-
models.flatten!
|
149
|
-
models.collect! { |model| model.is_a?(Symbol) ? model.to_s.camelize.constantize : model }
|
150
|
-
define_method(:observed_classes) { Set.new(models) }
|
151
|
-
end
|
152
|
-
|
153
|
-
# The class observed by default is inferred from the observer's class name:
|
154
|
-
# assert_equal Person, PersonObserver.observed_class
|
155
|
-
def observed_class
|
156
|
-
if observed_class_name = name[/(.*)Observer/, 1]
|
157
|
-
observed_class_name.constantize
|
158
|
-
else
|
159
|
-
nil
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
# Start observing the declared classes and their subclasses.
|
165
|
-
def initialize
|
166
|
-
Set.new(observed_classes + observed_subclasses).each { |klass| add_observer! klass }
|
167
|
-
end
|
168
|
-
|
169
|
-
# Send observed_method(object) if the method exists.
|
170
|
-
def update(observed_method, object) #:nodoc:
|
171
|
-
send(observed_method, object) if respond_to?(observed_method)
|
172
|
-
end
|
173
|
-
|
174
|
-
# Special method sent by the observed class when it is inherited.
|
175
|
-
# Passes the new subclass.
|
176
|
-
def observed_class_inherited(subclass) #:nodoc:
|
177
|
-
self.class.observe(observed_classes + [subclass])
|
178
|
-
add_observer!(subclass)
|
179
|
-
end
|
91
|
+
class Observer < ActiveModel::Observer
|
180
92
|
|
181
93
|
protected
|
94
|
+
|
182
95
|
def observed_classes
|
183
|
-
|
96
|
+
klasses = super
|
97
|
+
klasses + klasses.map { |klass| klass.descendants }.flatten
|
184
98
|
end
|
185
99
|
|
186
|
-
def
|
187
|
-
|
100
|
+
def add_observer!(klass)
|
101
|
+
super
|
102
|
+
define_callbacks klass
|
188
103
|
end
|
189
104
|
|
190
|
-
def
|
191
|
-
|
192
|
-
|
193
|
-
|
105
|
+
def define_callbacks(klass)
|
106
|
+
observer = self
|
107
|
+
observer_name = observer.class.name.underscore.gsub('/', '__')
|
108
|
+
|
109
|
+
ActiveRecord::Callbacks::CALLBACKS.each do |callback|
|
110
|
+
next unless respond_to?(callback)
|
111
|
+
callback_meth = :"_notify_#{observer_name}_for_#{callback}"
|
112
|
+
unless klass.respond_to?(callback_meth)
|
113
|
+
klass.send(:define_method, callback_meth) do |&block|
|
114
|
+
observer.update(callback, self, &block)
|
115
|
+
end
|
116
|
+
klass.send(callback, callback_meth)
|
117
|
+
end
|
194
118
|
end
|
195
119
|
end
|
196
120
|
end
|