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
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module AttributeAssignment
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include ActiveModel::MassAssignmentSecurity
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
private
|
10
|
+
|
11
|
+
# The primary key and inheritance column can never be set by mass-assignment for security reasons.
|
12
|
+
def attributes_protected_by_default
|
13
|
+
default = [ primary_key, inheritance_column ]
|
14
|
+
default << 'id' unless primary_key.eql? 'id'
|
15
|
+
default
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Allows you to set all the attributes at once by passing in a hash with keys
|
20
|
+
# matching the attribute names (which again matches the column names).
|
21
|
+
#
|
22
|
+
# If any attributes are protected by either +attr_protected+ or
|
23
|
+
# +attr_accessible+ then only settable attributes will be assigned.
|
24
|
+
#
|
25
|
+
# class User < ActiveRecord::Base
|
26
|
+
# attr_protected :is_admin
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# user = User.new
|
30
|
+
# user.attributes = { :username => 'Phusion', :is_admin => true }
|
31
|
+
# user.username # => "Phusion"
|
32
|
+
# user.is_admin? # => false
|
33
|
+
def attributes=(new_attributes)
|
34
|
+
return unless new_attributes.is_a?(Hash)
|
35
|
+
|
36
|
+
assign_attributes(new_attributes)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Allows you to set all the attributes for a particular mass-assignment
|
40
|
+
# security role by passing in a hash of attributes with keys matching
|
41
|
+
# the attribute names (which again matches the column names) and the role
|
42
|
+
# name using the :as option.
|
43
|
+
#
|
44
|
+
# To bypass mass-assignment security you can use the :without_protection => true
|
45
|
+
# option.
|
46
|
+
#
|
47
|
+
# class User < ActiveRecord::Base
|
48
|
+
# attr_accessible :name
|
49
|
+
# attr_accessible :name, :is_admin, :as => :admin
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# user = User.new
|
53
|
+
# user.assign_attributes({ :name => 'Josh', :is_admin => true })
|
54
|
+
# user.name # => "Josh"
|
55
|
+
# user.is_admin? # => false
|
56
|
+
#
|
57
|
+
# user = User.new
|
58
|
+
# user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin)
|
59
|
+
# user.name # => "Josh"
|
60
|
+
# user.is_admin? # => true
|
61
|
+
#
|
62
|
+
# user = User.new
|
63
|
+
# user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
|
64
|
+
# user.name # => "Josh"
|
65
|
+
# user.is_admin? # => true
|
66
|
+
def assign_attributes(new_attributes, options = {})
|
67
|
+
return if new_attributes.blank?
|
68
|
+
|
69
|
+
attributes = new_attributes.stringify_keys
|
70
|
+
multi_parameter_attributes = []
|
71
|
+
nested_parameter_attributes = []
|
72
|
+
@mass_assignment_options = options
|
73
|
+
|
74
|
+
unless options[:without_protection]
|
75
|
+
attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role)
|
76
|
+
end
|
77
|
+
|
78
|
+
attributes.each do |k, v|
|
79
|
+
if k.include?("(")
|
80
|
+
multi_parameter_attributes << [ k, v ]
|
81
|
+
elsif respond_to?("#{k}=")
|
82
|
+
if v.is_a?(Hash)
|
83
|
+
nested_parameter_attributes << [ k, v ]
|
84
|
+
else
|
85
|
+
send("#{k}=", v)
|
86
|
+
end
|
87
|
+
else
|
88
|
+
raise(UnknownAttributeError, "unknown attribute: #{k}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# assign any deferred nested attributes after the base attributes have been set
|
93
|
+
nested_parameter_attributes.each do |k,v|
|
94
|
+
send("#{k}=", v)
|
95
|
+
end
|
96
|
+
|
97
|
+
@mass_assignment_options = nil
|
98
|
+
assign_multiparameter_attributes(multi_parameter_attributes)
|
99
|
+
end
|
100
|
+
|
101
|
+
protected
|
102
|
+
|
103
|
+
def mass_assignment_options
|
104
|
+
@mass_assignment_options ||= {}
|
105
|
+
end
|
106
|
+
|
107
|
+
def mass_assignment_role
|
108
|
+
mass_assignment_options[:as] || :default
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
|
114
|
+
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
|
115
|
+
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
|
116
|
+
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
|
117
|
+
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum,
|
118
|
+
# f for Float, s for String, and a for Array. If all the values for a given attribute are empty, the
|
119
|
+
# attribute will be set to nil.
|
120
|
+
def assign_multiparameter_attributes(pairs)
|
121
|
+
execute_callstack_for_multiparameter_attributes(
|
122
|
+
extract_callstack_for_multiparameter_attributes(pairs)
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
def instantiate_time_object(name, values)
|
127
|
+
if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name))
|
128
|
+
Time.zone.local(*values)
|
129
|
+
else
|
130
|
+
Time.time_with_datetime_fallback(self.class.default_timezone, *values)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def execute_callstack_for_multiparameter_attributes(callstack)
|
135
|
+
errors = []
|
136
|
+
callstack.each do |name, values_with_empty_parameters|
|
137
|
+
begin
|
138
|
+
send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
|
139
|
+
rescue => ex
|
140
|
+
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name}", ex, name)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
unless errors.empty?
|
144
|
+
raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def read_value_from_parameter(name, values_hash_from_param)
|
149
|
+
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
|
150
|
+
if values_hash_from_param.values.all?{|v|v.nil?}
|
151
|
+
nil
|
152
|
+
elsif klass == Time
|
153
|
+
read_time_parameter_value(name, values_hash_from_param)
|
154
|
+
elsif klass == Date
|
155
|
+
read_date_parameter_value(name, values_hash_from_param)
|
156
|
+
else
|
157
|
+
read_other_parameter_value(klass, name, values_hash_from_param)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def read_time_parameter_value(name, values_hash_from_param)
|
162
|
+
# If Date bits were not provided, error
|
163
|
+
raise "Missing Parameter" if [1,2,3].any?{|position| !values_hash_from_param.has_key?(position)}
|
164
|
+
max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
|
165
|
+
# If Date bits were provided but blank, then return nil
|
166
|
+
return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
|
167
|
+
|
168
|
+
set_values = (1..max_position).collect{|position| values_hash_from_param[position] }
|
169
|
+
# If Time bits are not there, then default to 0
|
170
|
+
(3..5).each {|i| set_values[i] = set_values[i].blank? ? 0 : set_values[i]}
|
171
|
+
instantiate_time_object(name, set_values)
|
172
|
+
end
|
173
|
+
|
174
|
+
def read_date_parameter_value(name, values_hash_from_param)
|
175
|
+
return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
|
176
|
+
set_values = [values_hash_from_param[1], values_hash_from_param[2], values_hash_from_param[3]]
|
177
|
+
begin
|
178
|
+
Date.new(*set_values)
|
179
|
+
rescue ArgumentError # if Date.new raises an exception on an invalid date
|
180
|
+
instantiate_time_object(name, set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def read_other_parameter_value(klass, name, values_hash_from_param)
|
185
|
+
max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param)
|
186
|
+
values = (1..max_position).collect do |position|
|
187
|
+
raise "Missing Parameter" if !values_hash_from_param.has_key?(position)
|
188
|
+
values_hash_from_param[position]
|
189
|
+
end
|
190
|
+
klass.new(*values)
|
191
|
+
end
|
192
|
+
|
193
|
+
def extract_max_param_for_multiparameter_attributes(values_hash_from_param, upper_cap = 100)
|
194
|
+
[values_hash_from_param.keys.max,upper_cap].min
|
195
|
+
end
|
196
|
+
|
197
|
+
def extract_callstack_for_multiparameter_attributes(pairs)
|
198
|
+
attributes = { }
|
199
|
+
|
200
|
+
pairs.each do |pair|
|
201
|
+
multiparameter_name, value = pair
|
202
|
+
attribute_name = multiparameter_name.split("(").first
|
203
|
+
attributes[attribute_name] = {} unless attributes.include?(attribute_name)
|
204
|
+
|
205
|
+
parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
|
206
|
+
attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
|
207
|
+
end
|
208
|
+
|
209
|
+
attributes
|
210
|
+
end
|
211
|
+
|
212
|
+
def type_cast_attribute_value(multiparameter_name, value)
|
213
|
+
multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
|
214
|
+
end
|
215
|
+
|
216
|
+
def find_parameter_position(multiparameter_name)
|
217
|
+
multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module BeforeTypeCast
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
attribute_method_suffix "_before_type_cast"
|
8
|
+
end
|
9
|
+
|
10
|
+
def read_attribute_before_type_cast(attr_name)
|
11
|
+
@attributes[attr_name]
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns a hash of attributes before typecasting and deserialization.
|
15
|
+
def attributes_before_type_cast
|
16
|
+
@attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Handle *_before_type_cast for method_missing.
|
22
|
+
def attribute_before_type_cast(attribute_name)
|
23
|
+
if attribute_name == 'id'
|
24
|
+
read_attribute_before_type_cast(self.class.primary_key)
|
25
|
+
else
|
26
|
+
read_attribute_before_type_cast(attribute_name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/deprecation'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module AttributeMethods
|
6
|
+
module DeprecatedUnderscoreRead
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
attribute_method_prefix "_"
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
protected
|
15
|
+
|
16
|
+
def define_method__attribute(attr_name)
|
17
|
+
# Do nothing, let it hit method missing instead.
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def _attribute(attr_name)
|
24
|
+
ActiveSupport::Deprecation.warn(
|
25
|
+
"You have called '_#{attr_name}'. This is deprecated. Please use " \
|
26
|
+
"either '#{attr_name}' or read_attribute('#{attr_name}')."
|
27
|
+
)
|
28
|
+
read_attribute(attr_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
2
|
+
require 'active_support/core_ext/object/blank'
|
3
|
+
|
4
|
+
module ActiveRecord
|
5
|
+
module AttributeMethods
|
6
|
+
module Dirty
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
include ActiveModel::Dirty
|
9
|
+
include AttributeMethods::Write
|
10
|
+
|
11
|
+
included do
|
12
|
+
if self < ::ActiveRecord::Timestamp
|
13
|
+
raise "You cannot include Dirty after Timestamp"
|
14
|
+
end
|
15
|
+
|
16
|
+
class_attribute :partial_updates
|
17
|
+
self.partial_updates = true
|
18
|
+
end
|
19
|
+
|
20
|
+
# Attempts to +save+ the record and clears changed attributes if successful.
|
21
|
+
def save(*) #:nodoc:
|
22
|
+
if status = super
|
23
|
+
@previously_changed = changes
|
24
|
+
@changed_attributes.clear
|
25
|
+
elsif IdentityMap.enabled?
|
26
|
+
IdentityMap.remove(self)
|
27
|
+
end
|
28
|
+
status
|
29
|
+
end
|
30
|
+
|
31
|
+
# Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
|
32
|
+
def save!(*) #:nodoc:
|
33
|
+
super.tap do
|
34
|
+
@previously_changed = changes
|
35
|
+
@changed_attributes.clear
|
36
|
+
end
|
37
|
+
rescue
|
38
|
+
IdentityMap.remove(self) if IdentityMap.enabled?
|
39
|
+
raise
|
40
|
+
end
|
41
|
+
|
42
|
+
# <tt>reload</tt> the record and clears changed attributes.
|
43
|
+
def reload(*) #:nodoc:
|
44
|
+
super.tap do
|
45
|
+
@previously_changed.clear
|
46
|
+
@changed_attributes.clear
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
# Wrap write_attribute to remember original attribute value.
|
52
|
+
def write_attribute(attr, value)
|
53
|
+
attr = attr.to_s
|
54
|
+
|
55
|
+
# The attribute already has an unsaved change.
|
56
|
+
if attribute_changed?(attr)
|
57
|
+
old = @changed_attributes[attr]
|
58
|
+
@changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
|
59
|
+
else
|
60
|
+
old = clone_attribute_value(:read_attribute, attr)
|
61
|
+
# Save Time objects as TimeWithZone if time_zone_aware_attributes == true
|
62
|
+
old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
|
63
|
+
@changed_attributes[attr] = old if _field_changed?(attr, old, value)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Carry on.
|
67
|
+
super(attr, value)
|
68
|
+
end
|
69
|
+
|
70
|
+
def update(*)
|
71
|
+
if partial_updates?
|
72
|
+
# Serialized attributes should always be written in case they've been
|
73
|
+
# changed in place.
|
74
|
+
super(changed | (attributes.keys & self.class.serialized_attributes.keys))
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def _field_changed?(attr, old, value)
|
81
|
+
if column = column_for_attribute(attr)
|
82
|
+
if column.number? && (changes_from_nil_to_empty_string?(column, old, value) ||
|
83
|
+
changes_from_zero_to_string?(old, value))
|
84
|
+
value = nil
|
85
|
+
else
|
86
|
+
value = column.type_cast(value)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
old != value
|
91
|
+
end
|
92
|
+
|
93
|
+
def clone_with_time_zone_conversion_attribute?(attr, old)
|
94
|
+
old.class.name == "Time" && time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
|
95
|
+
end
|
96
|
+
|
97
|
+
def changes_from_nil_to_empty_string?(column, old, value)
|
98
|
+
# For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
|
99
|
+
# Hence we don't record it as a change if the value changes from nil to ''.
|
100
|
+
# If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
|
101
|
+
# be typecast back to 0 (''.to_i => 0)
|
102
|
+
column.null && (old.nil? || old == 0) && value.blank?
|
103
|
+
end
|
104
|
+
|
105
|
+
def changes_from_zero_to_string?(old, value)
|
106
|
+
# For columns with old 0 and value non-empty string
|
107
|
+
old == 0 && value.is_a?(String) && value.present? && value != '0'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module PrimaryKey
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
# Returns this record's primary key value wrapped in an Array if one is available
|
7
|
+
def to_key
|
8
|
+
key = self.id
|
9
|
+
[key] if key
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the primary key value
|
13
|
+
def id
|
14
|
+
read_attribute(self.class.primary_key)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Sets the primary key value
|
18
|
+
def id=(value)
|
19
|
+
write_attribute(self.class.primary_key, value)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Queries the primary key value
|
23
|
+
def id?
|
24
|
+
query_attribute(self.class.primary_key)
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def define_method_attribute(attr_name)
|
29
|
+
super
|
30
|
+
|
31
|
+
if attr_name == primary_key && attr_name != 'id'
|
32
|
+
generated_attribute_methods.send(:alias_method, :id, primary_key)
|
33
|
+
generated_external_attribute_methods.module_eval <<-CODE, __FILE__, __LINE__
|
34
|
+
def id(v, attributes, attributes_cache, attr_name)
|
35
|
+
attr_name = '#{primary_key}'
|
36
|
+
send(attr_name, attributes[attr_name], attributes, attributes_cache, attr_name)
|
37
|
+
end
|
38
|
+
CODE
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def dangerous_attribute_method?(method_name)
|
43
|
+
super && !['id', 'id=', 'id?'].include?(method_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
|
47
|
+
# primary_key_prefix_type setting, though.
|
48
|
+
def primary_key
|
49
|
+
@primary_key = reset_primary_key unless defined? @primary_key
|
50
|
+
@primary_key
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns a quoted version of the primary key name, used to construct SQL statements.
|
54
|
+
def quoted_primary_key
|
55
|
+
@quoted_primary_key ||= connection.quote_column_name(primary_key)
|
56
|
+
end
|
57
|
+
|
58
|
+
def reset_primary_key #:nodoc:
|
59
|
+
if self == base_class
|
60
|
+
self.primary_key = get_primary_key(base_class.name)
|
61
|
+
else
|
62
|
+
self.primary_key = base_class.primary_key
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_primary_key(base_name) #:nodoc:
|
67
|
+
return 'id' unless base_name && !base_name.blank?
|
68
|
+
|
69
|
+
case primary_key_prefix_type
|
70
|
+
when :table_name
|
71
|
+
base_name.foreign_key(false)
|
72
|
+
when :table_name_with_underscore
|
73
|
+
base_name.foreign_key
|
74
|
+
else
|
75
|
+
if ActiveRecord::Base != self && table_exists?
|
76
|
+
connection.schema_cache.primary_keys[table_name]
|
77
|
+
else
|
78
|
+
'id'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def original_primary_key #:nodoc:
|
84
|
+
deprecated_original_property_getter :primary_key
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sets the name of the primary key column.
|
88
|
+
#
|
89
|
+
# class Project < ActiveRecord::Base
|
90
|
+
# self.primary_key = "sysid"
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# You can also define the primary_key method yourself:
|
94
|
+
#
|
95
|
+
# class Project < ActiveRecord::Base
|
96
|
+
# def self.primary_key
|
97
|
+
# "foo_" + super
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
# Project.primary_key # => "foo_id"
|
101
|
+
def primary_key=(value)
|
102
|
+
@original_primary_key = @primary_key if defined?(@primary_key)
|
103
|
+
@primary_key = value && value.to_s
|
104
|
+
@quoted_primary_key = nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_primary_key(value = nil, &block) #:nodoc:
|
108
|
+
deprecated_property_setter :primary_key, value, block
|
109
|
+
@quoted_primary_key = nil
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module AttributeMethods
|
5
|
+
module Query
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
attribute_method_suffix "?"
|
10
|
+
end
|
11
|
+
|
12
|
+
def query_attribute(attr_name)
|
13
|
+
unless value = read_attribute(attr_name)
|
14
|
+
false
|
15
|
+
else
|
16
|
+
column = self.class.columns_hash[attr_name]
|
17
|
+
if column.nil?
|
18
|
+
if Numeric === value || value !~ /[^0-9]/
|
19
|
+
!value.to_i.zero?
|
20
|
+
else
|
21
|
+
return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
|
22
|
+
!value.blank?
|
23
|
+
end
|
24
|
+
elsif column.number?
|
25
|
+
!value.zero?
|
26
|
+
else
|
27
|
+
!value.blank?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
# Handle *? for method_missing.
|
34
|
+
def attribute?(attribute_name)
|
35
|
+
query_attribute(attribute_name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Read
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
|
7
|
+
|
8
|
+
included do
|
9
|
+
cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
|
10
|
+
self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# +cache_attributes+ allows you to declare which converted attribute values should
|
15
|
+
# be cached. Usually caching only pays off for attributes with expensive conversion
|
16
|
+
# methods, like time related columns (e.g. +created_at+, +updated_at+).
|
17
|
+
def cache_attributes(*attribute_names)
|
18
|
+
cached_attributes.merge attribute_names.map { |attr| attr.to_s }
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the attributes which are cached. By default time related columns
|
22
|
+
# with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
|
23
|
+
def cached_attributes
|
24
|
+
@cached_attributes ||= columns.select { |c| cacheable_column?(c) }.map { |col| col.name }.to_set
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns +true+ if the provided attribute is being cached.
|
28
|
+
def cache_attribute?(attr_name)
|
29
|
+
cached_attributes.include?(attr_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def undefine_attribute_methods
|
33
|
+
generated_external_attribute_methods.module_eval do
|
34
|
+
instance_methods.each { |m| undef_method(m) }
|
35
|
+
end
|
36
|
+
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def type_cast_attribute(attr_name, attributes, cache = {}) #:nodoc:
|
41
|
+
return unless attr_name
|
42
|
+
attr_name = attr_name.to_s
|
43
|
+
|
44
|
+
if generated_external_attribute_methods.method_defined?(attr_name)
|
45
|
+
if attributes.has_key?(attr_name) || attr_name == 'id'
|
46
|
+
generated_external_attribute_methods.send(attr_name, attributes[attr_name], attributes, cache, attr_name)
|
47
|
+
end
|
48
|
+
elsif !attribute_methods_generated?
|
49
|
+
# If we haven't generated the caster methods yet, do that and
|
50
|
+
# then try again
|
51
|
+
define_attribute_methods
|
52
|
+
type_cast_attribute(attr_name, attributes, cache)
|
53
|
+
else
|
54
|
+
# If we get here, the attribute has no associated DB column, so
|
55
|
+
# just return it verbatim.
|
56
|
+
attributes[attr_name]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
# We want to generate the methods via module_eval rather than define_method,
|
62
|
+
# because define_method is slower on dispatch and uses more memory (because it
|
63
|
+
# creates a closure).
|
64
|
+
#
|
65
|
+
# But sometimes the database might return columns with characters that are not
|
66
|
+
# allowed in normal method names (like 'my_column(omg)'. So to work around this
|
67
|
+
# we first define with the __temp__ identifier, and then use alias method to
|
68
|
+
# rename it to what we want.
|
69
|
+
def define_method_attribute(attr_name)
|
70
|
+
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
71
|
+
def __temp__
|
72
|
+
#{internal_attribute_access_code(attr_name, attribute_cast_code(attr_name))}
|
73
|
+
end
|
74
|
+
alias_method '#{attr_name}', :__temp__
|
75
|
+
undef_method :__temp__
|
76
|
+
STR
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def define_external_attribute_method(attr_name)
|
82
|
+
generated_external_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
83
|
+
def __temp__(v, attributes, attributes_cache, attr_name)
|
84
|
+
#{external_attribute_access_code(attr_name, attribute_cast_code(attr_name))}
|
85
|
+
end
|
86
|
+
alias_method '#{attr_name}', :__temp__
|
87
|
+
undef_method :__temp__
|
88
|
+
STR
|
89
|
+
end
|
90
|
+
|
91
|
+
def cacheable_column?(column)
|
92
|
+
attribute_types_cached_by_default.include?(column.type)
|
93
|
+
end
|
94
|
+
|
95
|
+
def internal_attribute_access_code(attr_name, cast_code)
|
96
|
+
access_code = "(v=@attributes[attr_name]) && #{cast_code}"
|
97
|
+
|
98
|
+
unless attr_name == primary_key
|
99
|
+
access_code.insert(0, "missing_attribute(attr_name, caller) unless @attributes.has_key?(attr_name); ")
|
100
|
+
end
|
101
|
+
|
102
|
+
if cache_attribute?(attr_name)
|
103
|
+
access_code = "@attributes_cache[attr_name] ||= (#{access_code})"
|
104
|
+
end
|
105
|
+
|
106
|
+
"attr_name = '#{attr_name}'; #{access_code}"
|
107
|
+
end
|
108
|
+
|
109
|
+
def external_attribute_access_code(attr_name, cast_code)
|
110
|
+
access_code = "v && #{cast_code}"
|
111
|
+
|
112
|
+
if cache_attribute?(attr_name)
|
113
|
+
access_code = "attributes_cache[attr_name] ||= (#{access_code})"
|
114
|
+
end
|
115
|
+
|
116
|
+
access_code
|
117
|
+
end
|
118
|
+
|
119
|
+
def attribute_cast_code(attr_name)
|
120
|
+
columns_hash[attr_name].type_cast_code('v')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
125
|
+
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
|
126
|
+
def read_attribute(attr_name)
|
127
|
+
self.class.type_cast_attribute(attr_name, @attributes, @attributes_cache)
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def attribute(attribute_name)
|
132
|
+
read_attribute(attribute_name)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|