activerecord 2.3.18 → 3.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +105 -34
- data/examples/performance.rb +3 -39
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +81 -47
- data/lib/active_record/aggregations.rb +1 -3
- data/lib/active_record/association_preload.rb +39 -54
- data/lib/active_record/associations.rb +262 -419
- data/lib/active_record/associations/association_collection.rb +85 -100
- data/lib/active_record/associations/association_proxy.rb +20 -18
- data/lib/active_record/associations/belongs_to_association.rb +8 -8
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -35
- data/lib/active_record/associations/has_many_association.rb +11 -19
- data/lib/active_record/associations/has_many_through_association.rb +30 -183
- data/lib/active_record/associations/has_one_association.rb +10 -10
- data/lib/active_record/associations/has_one_through_association.rb +13 -11
- data/lib/active_record/associations/through_association_scope.rb +153 -0
- data/lib/active_record/attribute_methods.rb +17 -370
- data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
- data/lib/active_record/attribute_methods/dirty.rb +87 -0
- data/lib/active_record/attribute_methods/primary_key.rb +44 -0
- data/lib/active_record/attribute_methods/query.rb +37 -0
- data/lib/active_record/attribute_methods/read.rb +116 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +60 -0
- data/lib/active_record/attribute_methods/write.rb +37 -0
- data/lib/active_record/autosave_association.rb +20 -41
- data/lib/active_record/base.rb +357 -1180
- data/lib/active_record/batches.rb +10 -16
- data/lib/active_record/callbacks.rb +66 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +17 -13
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +5 -25
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -72
- data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -49
- data/lib/active_record/connection_adapters/mysql_adapter.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +84 -46
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +9 -3
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +34 -65
- data/lib/active_record/fixtures.rb +21 -25
- data/lib/active_record/locale/en.yml +9 -27
- data/lib/active_record/locking/optimistic.rb +16 -48
- data/lib/active_record/migration.rb +59 -46
- data/lib/active_record/named_scope.rb +85 -92
- data/lib/active_record/nested_attributes.rb +18 -24
- data/lib/active_record/observer.rb +18 -94
- data/lib/active_record/railtie.rb +83 -0
- data/lib/active_record/railties/controller_runtime.rb +38 -0
- data/lib/active_record/railties/databases.rake +477 -0
- data/lib/active_record/railties/subscriber.rb +27 -0
- data/lib/active_record/reflection.rb +2 -15
- data/lib/active_record/relation.rb +339 -0
- data/lib/active_record/relation/calculations.rb +259 -0
- data/lib/active_record/relation/finder_methods.rb +315 -0
- data/lib/active_record/relation/predicate_builder.rb +46 -0
- data/lib/active_record/relation/query_methods.rb +218 -0
- data/lib/active_record/relation/spawn_methods.rb +102 -0
- data/lib/active_record/schema_dumper.rb +10 -6
- data/lib/active_record/serialization.rb +31 -74
- data/lib/active_record/serializers/xml_serializer.rb +33 -121
- data/lib/active_record/session_store.rb +1 -9
- data/lib/active_record/test_case.rb +1 -3
- data/lib/active_record/timestamp.rb +7 -5
- data/lib/active_record/transactions.rb +9 -9
- data/lib/active_record/validations.rb +51 -1100
- data/lib/active_record/validations/associated.rb +47 -0
- data/lib/active_record/validations/uniqueness.rb +181 -0
- data/lib/active_record/version.rb +3 -3
- data/lib/generators/active_record.rb +30 -0
- data/lib/generators/active_record/migration/migration_generator.rb +25 -0
- data/lib/generators/active_record/migration/templates/migration.rb +11 -0
- data/lib/generators/active_record/model/model_generator.rb +33 -0
- data/lib/generators/active_record/model/templates/migration.rb +16 -0
- data/lib/generators/active_record/model/templates/model.rb +5 -0
- data/lib/generators/active_record/observer/observer_generator.rb +15 -0
- data/lib/generators/active_record/observer/templates/observer.rb +2 -0
- data/lib/generators/active_record/session_migration/session_migration_generator.rb +24 -0
- data/lib/generators/active_record/session_migration/templates/migration.rb +16 -0
- metadata +67 -325
- data/RUNNING_UNIT_TESTS +0 -36
- data/Rakefile +0 -268
- data/install.rb +0 -30
- data/lib/active_record/calculations.rb +0 -321
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -57
- data/lib/active_record/dirty.rb +0 -183
- data/lib/active_record/serializers/json_serializer.rb +0 -91
- data/lib/activerecord.rb +0 -2
- data/test/assets/example.log +0 -1
- data/test/assets/flowers.jpg +0 -0
- data/test/cases/aaa_create_tables_test.rb +0 -24
- data/test/cases/active_schema_test_mysql.rb +0 -122
- data/test/cases/active_schema_test_postgresql.rb +0 -24
- data/test/cases/adapter_test.rb +0 -144
- data/test/cases/aggregations_test.rb +0 -167
- data/test/cases/ar_schema_test.rb +0 -32
- data/test/cases/associations/belongs_to_associations_test.rb +0 -438
- data/test/cases/associations/callbacks_test.rb +0 -161
- data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
- data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
- data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
- data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
- data/test/cases/associations/eager_singularization_test.rb +0 -145
- data/test/cases/associations/eager_test.rb +0 -852
- data/test/cases/associations/extension_test.rb +0 -62
- data/test/cases/associations/habtm_join_table_test.rb +0 -56
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
- data/test/cases/associations/has_many_associations_test.rb +0 -1273
- data/test/cases/associations/has_many_through_associations_test.rb +0 -360
- data/test/cases/associations/has_one_associations_test.rb +0 -330
- data/test/cases/associations/has_one_through_associations_test.rb +0 -209
- data/test/cases/associations/inner_join_association_test.rb +0 -93
- data/test/cases/associations/inverse_associations_test.rb +0 -566
- data/test/cases/associations/join_model_test.rb +0 -712
- data/test/cases/associations_test.rb +0 -282
- data/test/cases/attribute_methods_test.rb +0 -305
- data/test/cases/autosave_association_test.rb +0 -1218
- data/test/cases/base_test.rb +0 -2166
- data/test/cases/batches_test.rb +0 -81
- data/test/cases/binary_test.rb +0 -30
- data/test/cases/calculations_test.rb +0 -360
- data/test/cases/callbacks_observers_test.rb +0 -38
- data/test/cases/callbacks_test.rb +0 -438
- data/test/cases/class_inheritable_attributes_test.rb +0 -32
- data/test/cases/column_alias_test.rb +0 -17
- data/test/cases/column_definition_test.rb +0 -70
- data/test/cases/connection_pool_test.rb +0 -25
- data/test/cases/connection_test_firebird.rb +0 -8
- data/test/cases/connection_test_mysql.rb +0 -65
- data/test/cases/copy_table_test_sqlite.rb +0 -80
- data/test/cases/counter_cache_test.rb +0 -84
- data/test/cases/database_statements_test.rb +0 -12
- data/test/cases/datatype_test_postgresql.rb +0 -204
- data/test/cases/date_time_test.rb +0 -37
- data/test/cases/default_test_firebird.rb +0 -16
- data/test/cases/defaults_test.rb +0 -111
- data/test/cases/deprecated_finder_test.rb +0 -30
- data/test/cases/dirty_test.rb +0 -316
- data/test/cases/finder_respond_to_test.rb +0 -76
- data/test/cases/finder_test.rb +0 -1098
- data/test/cases/fixtures_test.rb +0 -661
- data/test/cases/helper.rb +0 -68
- data/test/cases/i18n_test.rb +0 -46
- data/test/cases/inheritance_test.rb +0 -262
- data/test/cases/invalid_date_test.rb +0 -24
- data/test/cases/json_serialization_test.rb +0 -219
- data/test/cases/lifecycle_test.rb +0 -193
- data/test/cases/locking_test.rb +0 -350
- data/test/cases/method_scoping_test.rb +0 -704
- data/test/cases/migration_test.rb +0 -1649
- data/test/cases/migration_test_firebird.rb +0 -124
- data/test/cases/mixin_test.rb +0 -96
- data/test/cases/modules_test.rb +0 -109
- data/test/cases/multiple_db_test.rb +0 -85
- data/test/cases/named_scope_test.rb +0 -372
- data/test/cases/nested_attributes_test.rb +0 -840
- data/test/cases/pk_test.rb +0 -119
- data/test/cases/pooled_connections_test.rb +0 -103
- data/test/cases/query_cache_test.rb +0 -129
- data/test/cases/readonly_test.rb +0 -107
- data/test/cases/reflection_test.rb +0 -234
- data/test/cases/reload_models_test.rb +0 -22
- data/test/cases/repair_helper.rb +0 -50
- data/test/cases/reserved_word_test_mysql.rb +0 -176
- data/test/cases/sanitize_test.rb +0 -25
- data/test/cases/schema_authorization_test_postgresql.rb +0 -75
- data/test/cases/schema_dumper_test.rb +0 -211
- data/test/cases/schema_test_postgresql.rb +0 -178
- data/test/cases/serialization_test.rb +0 -47
- data/test/cases/sp_test_mysql.rb +0 -16
- data/test/cases/synonym_test_oracle.rb +0 -17
- data/test/cases/timestamp_test.rb +0 -75
- data/test/cases/transactions_test.rb +0 -543
- data/test/cases/unconnected_test.rb +0 -32
- data/test/cases/validations_i18n_test.rb +0 -925
- data/test/cases/validations_test.rb +0 -1684
- data/test/cases/xml_serialization_test.rb +0 -240
- data/test/cases/yaml_serialization_test.rb +0 -11
- data/test/config.rb +0 -5
- data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
- data/test/connections/jdbc_jdbch2/connection.rb +0 -18
- data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
- data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
- data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
- data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
- data/test/connections/native_db2/connection.rb +0 -25
- data/test/connections/native_firebird/connection.rb +0 -26
- data/test/connections/native_frontbase/connection.rb +0 -27
- data/test/connections/native_mysql/connection.rb +0 -25
- data/test/connections/native_openbase/connection.rb +0 -21
- data/test/connections/native_oracle/connection.rb +0 -27
- data/test/connections/native_postgresql/connection.rb +0 -21
- data/test/connections/native_sqlite/connection.rb +0 -25
- data/test/connections/native_sqlite3/connection.rb +0 -25
- data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
- data/test/connections/native_sybase/connection.rb +0 -23
- data/test/fixtures/accounts.yml +0 -29
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author_addresses.yml +0 -5
- data/test/fixtures/author_favorites.yml +0 -4
- data/test/fixtures/authors.yml +0 -9
- data/test/fixtures/binaries.yml +0 -132
- data/test/fixtures/books.yml +0 -7
- data/test/fixtures/categories.yml +0 -14
- data/test/fixtures/categories/special_categories.yml +0 -9
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
- data/test/fixtures/categories_ordered.yml +0 -7
- data/test/fixtures/categories_posts.yml +0 -23
- data/test/fixtures/categorizations.yml +0 -17
- data/test/fixtures/clubs.yml +0 -6
- data/test/fixtures/comments.yml +0 -59
- data/test/fixtures/companies.yml +0 -56
- data/test/fixtures/computers.yml +0 -4
- data/test/fixtures/courses.yml +0 -7
- data/test/fixtures/customers.yml +0 -26
- data/test/fixtures/developers.yml +0 -21
- data/test/fixtures/developers_projects.yml +0 -17
- data/test/fixtures/edges.yml +0 -6
- data/test/fixtures/entrants.yml +0 -14
- data/test/fixtures/faces.yml +0 -11
- data/test/fixtures/fk_test_has_fk.yml +0 -3
- data/test/fixtures/fk_test_has_pk.yml +0 -2
- data/test/fixtures/funny_jokes.yml +0 -10
- data/test/fixtures/interests.yml +0 -33
- data/test/fixtures/items.yml +0 -4
- data/test/fixtures/jobs.yml +0 -7
- data/test/fixtures/legacy_things.yml +0 -3
- data/test/fixtures/mateys.yml +0 -4
- data/test/fixtures/member_types.yml +0 -6
- data/test/fixtures/members.yml +0 -6
- data/test/fixtures/memberships.yml +0 -20
- data/test/fixtures/men.yml +0 -5
- data/test/fixtures/minimalistics.yml +0 -2
- data/test/fixtures/mixed_case_monkeys.yml +0 -6
- data/test/fixtures/mixins.yml +0 -29
- data/test/fixtures/movies.yml +0 -7
- data/test/fixtures/naked/csv/accounts.csv +0 -1
- data/test/fixtures/naked/yml/accounts.yml +0 -1
- data/test/fixtures/naked/yml/companies.yml +0 -1
- data/test/fixtures/naked/yml/courses.yml +0 -1
- data/test/fixtures/organizations.yml +0 -5
- data/test/fixtures/owners.yml +0 -7
- data/test/fixtures/parrots.yml +0 -27
- data/test/fixtures/parrots_pirates.yml +0 -7
- data/test/fixtures/people.yml +0 -15
- data/test/fixtures/pets.yml +0 -14
- data/test/fixtures/pirates.yml +0 -9
- data/test/fixtures/polymorphic_designs.yml +0 -19
- data/test/fixtures/polymorphic_prices.yml +0 -19
- data/test/fixtures/posts.yml +0 -52
- data/test/fixtures/price_estimates.yml +0 -7
- data/test/fixtures/projects.yml +0 -7
- data/test/fixtures/readers.yml +0 -9
- data/test/fixtures/references.yml +0 -17
- data/test/fixtures/reserved_words/distinct.yml +0 -5
- data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
- data/test/fixtures/reserved_words/group.yml +0 -14
- data/test/fixtures/reserved_words/select.yml +0 -8
- data/test/fixtures/reserved_words/values.yml +0 -7
- data/test/fixtures/ships.yml +0 -5
- data/test/fixtures/sponsors.yml +0 -9
- data/test/fixtures/subscribers.yml +0 -7
- data/test/fixtures/subscriptions.yml +0 -12
- data/test/fixtures/taggings.yml +0 -28
- data/test/fixtures/tags.yml +0 -7
- data/test/fixtures/tasks.yml +0 -7
- data/test/fixtures/tees.yml +0 -4
- data/test/fixtures/ties.yml +0 -4
- data/test/fixtures/topics.yml +0 -42
- data/test/fixtures/toys.yml +0 -4
- data/test/fixtures/treasures.yml +0 -10
- data/test/fixtures/vertices.yml +0 -4
- data/test/fixtures/warehouse-things.yml +0 -3
- data/test/fixtures/zines.yml +0 -5
- data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
- data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
- data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
- data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
- data/test/migrations/duplicate/3_foo.rb +0 -7
- data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
- data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
- data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
- data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
- data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
- data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
- data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
- data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
- data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
- data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
- data/test/migrations/missing/1_people_have_last_names.rb +0 -9
- data/test/migrations/missing/3_we_need_reminders.rb +0 -12
- data/test/migrations/missing/4_innocent_jointable.rb +0 -12
- data/test/migrations/valid/1_people_have_last_names.rb +0 -9
- data/test/migrations/valid/2_we_need_reminders.rb +0 -12
- data/test/migrations/valid/3_innocent_jointable.rb +0 -12
- data/test/models/author.rb +0 -151
- data/test/models/auto_id.rb +0 -4
- data/test/models/binary.rb +0 -2
- data/test/models/bird.rb +0 -9
- data/test/models/book.rb +0 -4
- data/test/models/categorization.rb +0 -5
- data/test/models/category.rb +0 -34
- data/test/models/citation.rb +0 -6
- data/test/models/club.rb +0 -13
- data/test/models/column_name.rb +0 -3
- data/test/models/comment.rb +0 -29
- data/test/models/company.rb +0 -173
- data/test/models/company_in_module.rb +0 -78
- data/test/models/computer.rb +0 -3
- data/test/models/contact.rb +0 -16
- data/test/models/contract.rb +0 -5
- data/test/models/course.rb +0 -3
- data/test/models/customer.rb +0 -73
- data/test/models/default.rb +0 -2
- data/test/models/developer.rb +0 -101
- data/test/models/edge.rb +0 -5
- data/test/models/entrant.rb +0 -3
- data/test/models/essay.rb +0 -3
- data/test/models/event.rb +0 -3
- data/test/models/event_author.rb +0 -8
- data/test/models/face.rb +0 -7
- data/test/models/guid.rb +0 -2
- data/test/models/interest.rb +0 -5
- data/test/models/invoice.rb +0 -4
- data/test/models/item.rb +0 -7
- data/test/models/job.rb +0 -5
- data/test/models/joke.rb +0 -3
- data/test/models/keyboard.rb +0 -3
- data/test/models/legacy_thing.rb +0 -3
- data/test/models/line_item.rb +0 -3
- data/test/models/man.rb +0 -9
- data/test/models/matey.rb +0 -4
- data/test/models/member.rb +0 -12
- data/test/models/member_detail.rb +0 -5
- data/test/models/member_type.rb +0 -3
- data/test/models/membership.rb +0 -9
- data/test/models/minimalistic.rb +0 -2
- data/test/models/mixed_case_monkey.rb +0 -3
- data/test/models/movie.rb +0 -5
- data/test/models/order.rb +0 -4
- data/test/models/organization.rb +0 -6
- data/test/models/owner.rb +0 -5
- data/test/models/parrot.rb +0 -22
- data/test/models/person.rb +0 -16
- data/test/models/pet.rb +0 -5
- data/test/models/pirate.rb +0 -80
- data/test/models/polymorphic_design.rb +0 -3
- data/test/models/polymorphic_price.rb +0 -3
- data/test/models/post.rb +0 -102
- data/test/models/price_estimate.rb +0 -3
- data/test/models/project.rb +0 -30
- data/test/models/reader.rb +0 -4
- data/test/models/reference.rb +0 -4
- data/test/models/reply.rb +0 -46
- data/test/models/ship.rb +0 -19
- data/test/models/ship_part.rb +0 -7
- data/test/models/sponsor.rb +0 -4
- data/test/models/subject.rb +0 -4
- data/test/models/subscriber.rb +0 -8
- data/test/models/subscription.rb +0 -4
- data/test/models/tag.rb +0 -7
- data/test/models/tagging.rb +0 -10
- data/test/models/task.rb +0 -3
- data/test/models/tee.rb +0 -4
- data/test/models/tie.rb +0 -4
- data/test/models/topic.rb +0 -80
- data/test/models/toy.rb +0 -6
- data/test/models/treasure.rb +0 -8
- data/test/models/vertex.rb +0 -9
- data/test/models/warehouse_thing.rb +0 -5
- data/test/models/zine.rb +0 -3
- data/test/schema/mysql_specific_schema.rb +0 -31
- data/test/schema/postgresql_specific_schema.rb +0 -114
- data/test/schema/schema.rb +0 -550
- data/test/schema/schema2.rb +0 -6
- data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -0,0 +1,87 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Dirty
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include ActiveModel::Dirty
|
6
|
+
|
7
|
+
included do
|
8
|
+
alias_method_chain :save, :dirty
|
9
|
+
alias_method_chain :save!, :dirty
|
10
|
+
alias_method_chain :update, :dirty
|
11
|
+
alias_method_chain :reload, :dirty
|
12
|
+
|
13
|
+
superclass_delegating_accessor :partial_updates
|
14
|
+
self.partial_updates = true
|
15
|
+
end
|
16
|
+
|
17
|
+
# Attempts to +save+ the record and clears changed attributes if successful.
|
18
|
+
def save_with_dirty(*args) #:nodoc:
|
19
|
+
if status = save_without_dirty(*args)
|
20
|
+
@previously_changed = changes
|
21
|
+
changed_attributes.clear
|
22
|
+
end
|
23
|
+
status
|
24
|
+
end
|
25
|
+
|
26
|
+
# Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
|
27
|
+
def save_with_dirty!(*args) #:nodoc:
|
28
|
+
save_without_dirty!(*args).tap do
|
29
|
+
@previously_changed = changes
|
30
|
+
changed_attributes.clear
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# <tt>reload</tt> the record and clears changed attributes.
|
35
|
+
def reload_with_dirty(*args) #:nodoc:
|
36
|
+
reload_without_dirty(*args).tap do
|
37
|
+
previously_changed_attributes.clear
|
38
|
+
changed_attributes.clear
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
# Wrap write_attribute to remember original attribute value.
|
44
|
+
def write_attribute(attr, value)
|
45
|
+
attr = attr.to_s
|
46
|
+
|
47
|
+
# The attribute already has an unsaved change.
|
48
|
+
if changed_attributes.include?(attr)
|
49
|
+
old = changed_attributes[attr]
|
50
|
+
changed_attributes.delete(attr) unless field_changed?(attr, old, value)
|
51
|
+
else
|
52
|
+
old = clone_attribute_value(:read_attribute, attr)
|
53
|
+
changed_attributes[attr] = old if field_changed?(attr, old, value)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Carry on.
|
57
|
+
super(attr, value)
|
58
|
+
end
|
59
|
+
|
60
|
+
def update_with_dirty
|
61
|
+
if partial_updates?
|
62
|
+
# Serialized attributes should always be written in case they've been
|
63
|
+
# changed in place.
|
64
|
+
update_without_dirty(changed | (attributes.keys & self.class.serialized_attributes.keys))
|
65
|
+
else
|
66
|
+
update_without_dirty
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def field_changed?(attr, old, value)
|
71
|
+
if column = column_for_attribute(attr)
|
72
|
+
if column.number? && column.null && (old.nil? || old == 0) && value.blank?
|
73
|
+
# For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
|
74
|
+
# Hence we don't record it as a change if the value changes from nil to ''.
|
75
|
+
# If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
|
76
|
+
# be typecast back to 0 (''.to_i => 0)
|
77
|
+
value = nil
|
78
|
+
else
|
79
|
+
value = column.type_cast(value)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
old != value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module PrimaryKey
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
# Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
|
8
|
+
# primary_key_prefix_type setting, though.
|
9
|
+
def primary_key
|
10
|
+
reset_primary_key
|
11
|
+
end
|
12
|
+
|
13
|
+
def reset_primary_key #:nodoc:
|
14
|
+
key = get_primary_key(base_class.name)
|
15
|
+
set_primary_key(key)
|
16
|
+
key
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_primary_key(base_name) #:nodoc:
|
20
|
+
key = 'id'
|
21
|
+
case primary_key_prefix_type
|
22
|
+
when :table_name
|
23
|
+
key = base_name.to_s.foreign_key(false)
|
24
|
+
when :table_name_with_underscore
|
25
|
+
key = base_name.to_s.foreign_key
|
26
|
+
end
|
27
|
+
key
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets the name of the primary key column to use to the given value,
|
31
|
+
# or (if the value is nil or false) to the value returned by the given
|
32
|
+
# block.
|
33
|
+
#
|
34
|
+
# class Project < ActiveRecord::Base
|
35
|
+
# set_primary_key "sysid"
|
36
|
+
# end
|
37
|
+
def set_primary_key(value = nil, &block)
|
38
|
+
define_attr_method :primary_key, value, &block
|
39
|
+
end
|
40
|
+
alias :primary_key= :set_primary_key
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Query
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
attribute_method_suffix "?"
|
8
|
+
end
|
9
|
+
|
10
|
+
def query_attribute(attr_name)
|
11
|
+
unless value = read_attribute(attr_name)
|
12
|
+
false
|
13
|
+
else
|
14
|
+
column = self.class.columns_hash[attr_name]
|
15
|
+
if column.nil?
|
16
|
+
if Numeric === value || value !~ /[^0-9]/
|
17
|
+
!value.to_i.zero?
|
18
|
+
else
|
19
|
+
return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
|
20
|
+
!value.blank?
|
21
|
+
end
|
22
|
+
elsif column.number?
|
23
|
+
!value.zero?
|
24
|
+
else
|
25
|
+
!value.blank?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
# Handle *? for method_missing.
|
32
|
+
def attribute?(attribute_name)
|
33
|
+
query_attribute(attribute_name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,116 @@
|
|
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
|
+
attribute_method_suffix ""
|
10
|
+
|
11
|
+
cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
|
12
|
+
self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
|
13
|
+
|
14
|
+
# Undefine id so it can be used as an attribute name
|
15
|
+
undef_method(:id) if method_defined?(:id)
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
# +cache_attributes+ allows you to declare which converted attribute values should
|
20
|
+
# be cached. Usually caching only pays off for attributes with expensive conversion
|
21
|
+
# methods, like time related columns (e.g. +created_at+, +updated_at+).
|
22
|
+
def cache_attributes(*attribute_names)
|
23
|
+
attribute_names.each {|attr| cached_attributes << attr.to_s}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the attributes which are cached. By default time related columns
|
27
|
+
# with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
|
28
|
+
def cached_attributes
|
29
|
+
@cached_attributes ||=
|
30
|
+
columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map{|col| col.name}.to_set
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns +true+ if the provided attribute is being cached.
|
34
|
+
def cache_attribute?(attr_name)
|
35
|
+
cached_attributes.include?(attr_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
def define_method_attribute(attr_name)
|
40
|
+
if self.serialized_attributes[attr_name]
|
41
|
+
define_read_method_for_serialized_attribute(attr_name)
|
42
|
+
else
|
43
|
+
define_read_method(attr_name.to_sym, attr_name, columns_hash[attr_name])
|
44
|
+
end
|
45
|
+
|
46
|
+
if attr_name == primary_key && attr_name != "id"
|
47
|
+
define_read_method(:id, attr_name, columns_hash[attr_name])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
# Define read method for serialized attribute.
|
53
|
+
def define_read_method_for_serialized_attribute(attr_name)
|
54
|
+
generated_attribute_methods.module_eval("def #{attr_name}; unserialize_attribute('#{attr_name}'); end", __FILE__, __LINE__)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Define an attribute reader method. Cope with nil column.
|
58
|
+
def define_read_method(symbol, attr_name, column)
|
59
|
+
cast_code = column.type_cast_code('v') if column
|
60
|
+
access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
|
61
|
+
|
62
|
+
unless attr_name.to_s == self.primary_key.to_s
|
63
|
+
access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
|
64
|
+
end
|
65
|
+
|
66
|
+
if cache_attribute?(attr_name)
|
67
|
+
access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
|
68
|
+
end
|
69
|
+
generated_attribute_methods.module_eval("def #{symbol}; #{access_code}; end", __FILE__, __LINE__)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
74
|
+
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
|
75
|
+
def read_attribute(attr_name)
|
76
|
+
attr_name = attr_name.to_s
|
77
|
+
attr_name = self.class.primary_key if attr_name == 'id'
|
78
|
+
if !(value = @attributes[attr_name]).nil?
|
79
|
+
if column = column_for_attribute(attr_name)
|
80
|
+
if unserializable_attribute?(attr_name, column)
|
81
|
+
unserialize_attribute(attr_name)
|
82
|
+
else
|
83
|
+
column.type_cast(value)
|
84
|
+
end
|
85
|
+
else
|
86
|
+
value
|
87
|
+
end
|
88
|
+
else
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns true if the attribute is of a text column and marked for serialization.
|
94
|
+
def unserializable_attribute?(attr_name, column)
|
95
|
+
column.text? && self.class.serialized_attributes[attr_name]
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns the unserialized object of the attribute.
|
99
|
+
def unserialize_attribute(attr_name)
|
100
|
+
unserialized_object = object_from_yaml(@attributes[attr_name])
|
101
|
+
|
102
|
+
if unserialized_object.is_a?(self.class.serialized_attributes[attr_name]) || unserialized_object.nil?
|
103
|
+
@attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object
|
104
|
+
else
|
105
|
+
raise SerializationTypeMismatch,
|
106
|
+
"#{attr_name} was supposed to be a #{self.class.serialized_attributes[attr_name]}, but was a #{unserialized_object.class.to_s}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
def attribute(attribute_name)
|
112
|
+
read_attribute(attribute_name)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module TimeZoneConversion
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
cattr_accessor :time_zone_aware_attributes, :instance_writer => false
|
8
|
+
self.time_zone_aware_attributes = false
|
9
|
+
|
10
|
+
class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
|
11
|
+
self.skip_time_zone_conversion_for_attributes = []
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
protected
|
16
|
+
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
|
17
|
+
# This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
|
18
|
+
def define_method_attribute(attr_name)
|
19
|
+
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
|
20
|
+
method_body = <<-EOV
|
21
|
+
def #{attr_name}(reload = false)
|
22
|
+
cached = @attributes_cache['#{attr_name}']
|
23
|
+
return cached if cached && !reload
|
24
|
+
time = read_attribute('#{attr_name}')
|
25
|
+
@attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
|
26
|
+
end
|
27
|
+
EOV
|
28
|
+
generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
|
35
|
+
# This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
|
36
|
+
def define_method_attribute=(attr_name)
|
37
|
+
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
|
38
|
+
method_body = <<-EOV
|
39
|
+
def #{attr_name}=(time)
|
40
|
+
unless time.acts_like?(:time)
|
41
|
+
time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
|
42
|
+
end
|
43
|
+
time = time.in_time_zone rescue nil if time
|
44
|
+
write_attribute(:#{attr_name}, time)
|
45
|
+
end
|
46
|
+
EOV
|
47
|
+
generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def create_time_zone_conversion_attribute?(name, column)
|
55
|
+
time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Write
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
attribute_method_suffix "="
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
protected
|
12
|
+
def define_method_attribute=(attr_name)
|
13
|
+
generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
|
18
|
+
# columns are turned into +nil+.
|
19
|
+
def write_attribute(attr_name, value)
|
20
|
+
attr_name = attr_name.to_s
|
21
|
+
attr_name = self.class.primary_key if attr_name == 'id'
|
22
|
+
@attributes_cache.delete(attr_name)
|
23
|
+
if (column = column_for_attribute(attr_name)) && column.number?
|
24
|
+
@attributes[attr_name] = convert_number_column_value(value)
|
25
|
+
else
|
26
|
+
@attributes[attr_name] = value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
# Handle *= for method_missing.
|
32
|
+
def attribute=(attribute_name, value)
|
33
|
+
write_attribute(attribute_name, value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -116,25 +116,24 @@ module ActiveRecord
|
|
116
116
|
# post = Post.find(1)
|
117
117
|
# post.author.name = ''
|
118
118
|
# post.save # => false
|
119
|
-
# post.errors # => #<ActiveRecord::Errors:0x174498c @errors={"
|
119
|
+
# post.errors # => #<ActiveRecord::Errors:0x174498c @errors={"author.name"=>["can't be blank"]}, @base=#<Post ...>>
|
120
120
|
#
|
121
121
|
# No validations will be performed on the associated models when validations
|
122
122
|
# are skipped for the parent:
|
123
123
|
#
|
124
124
|
# post = Post.find(1)
|
125
125
|
# post.author.name = ''
|
126
|
-
# post.save(false) # => true
|
126
|
+
# post.save(:validate => false) # => true
|
127
127
|
module AutosaveAssociation
|
128
|
+
extend ActiveSupport::Concern
|
129
|
+
|
128
130
|
ASSOCIATION_TYPES = %w{ has_one belongs_to has_many has_and_belongs_to_many }
|
129
131
|
|
130
|
-
|
131
|
-
|
132
|
-
base.extend(ClassMethods)
|
133
|
-
alias_method_chain :reload, :autosave_associations
|
132
|
+
included do
|
133
|
+
alias_method_chain :reload, :autosave_associations
|
134
134
|
|
135
|
-
|
136
|
-
|
137
|
-
end
|
135
|
+
ASSOCIATION_TYPES.each do |type|
|
136
|
+
send("valid_keys_for_#{type}_association") << :autosave
|
138
137
|
end
|
139
138
|
end
|
140
139
|
|
@@ -146,12 +145,12 @@ module ActiveRecord
|
|
146
145
|
# add_autosave_association_callbacks(reflect_on_association(name))
|
147
146
|
# end
|
148
147
|
ASSOCIATION_TYPES.each do |type|
|
149
|
-
module_eval
|
148
|
+
module_eval %{
|
150
149
|
def #{type}(name, options = {})
|
151
150
|
super
|
152
151
|
add_autosave_association_callbacks(reflect_on_association(name))
|
153
152
|
end
|
154
|
-
|
153
|
+
}
|
155
154
|
end
|
156
155
|
|
157
156
|
# Adds a validate and save callback for the association as specified by
|
@@ -217,12 +216,6 @@ module ActiveRecord
|
|
217
216
|
@marked_for_destruction
|
218
217
|
end
|
219
218
|
|
220
|
-
# Returns whether or not this record has been changed in any way (including whether
|
221
|
-
# any of its nested autosave associations are likewise changed)
|
222
|
-
def changed_for_autosave?
|
223
|
-
new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
|
224
|
-
end
|
225
|
-
|
226
219
|
private
|
227
220
|
|
228
221
|
# Returns the record for an association collection that should be validated
|
@@ -232,27 +225,12 @@ module ActiveRecord
|
|
232
225
|
if new_record
|
233
226
|
association
|
234
227
|
elsif autosave
|
235
|
-
association.target.
|
228
|
+
association.target.find_all { |record| record.new_record? || record.changed? || record.marked_for_destruction? }
|
236
229
|
else
|
237
|
-
association.target.
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
# go through nested autosave associations that are loaded in memory (without loading
|
242
|
-
# any new ones), and return true if is changed for autosave
|
243
|
-
def nested_records_changed_for_autosave?
|
244
|
-
self.class.reflect_on_all_autosave_associations.each do |reflection|
|
245
|
-
if association = association_instance_get(reflection.name)
|
246
|
-
if [:belongs_to, :has_one].include?(reflection.macro)
|
247
|
-
return true if association.target && association.target.changed_for_autosave?
|
248
|
-
else
|
249
|
-
association.target.each {|record| return true if record.changed_for_autosave? }
|
250
|
-
end
|
251
|
-
end
|
230
|
+
association.target.find_all { |record| record.new_record? }
|
252
231
|
end
|
253
|
-
false
|
254
232
|
end
|
255
|
-
|
233
|
+
|
256
234
|
# Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
|
257
235
|
# turned on for the association specified by +reflection+.
|
258
236
|
def validate_single_association(reflection)
|
@@ -280,9 +258,10 @@ module ActiveRecord
|
|
280
258
|
|
281
259
|
unless valid = association.valid?
|
282
260
|
if reflection.options[:autosave]
|
283
|
-
association.errors.
|
261
|
+
association.errors.each do |attribute, message|
|
284
262
|
attribute = "#{reflection.name}.#{attribute}"
|
285
|
-
errors
|
263
|
+
errors[attribute] << message
|
264
|
+
errors[attribute].uniq!
|
286
265
|
end
|
287
266
|
else
|
288
267
|
errors.add(reflection.name)
|
@@ -323,7 +302,7 @@ module ActiveRecord
|
|
323
302
|
association.send(:insert_record, record)
|
324
303
|
end
|
325
304
|
elsif autosave
|
326
|
-
saved = record.save(false)
|
305
|
+
saved = record.save(:validate => false)
|
327
306
|
end
|
328
307
|
|
329
308
|
raise ActiveRecord::Rollback if saved == false
|
@@ -353,7 +332,7 @@ module ActiveRecord
|
|
353
332
|
key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
|
354
333
|
if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave)
|
355
334
|
association[reflection.primary_key_name] = key
|
356
|
-
saved = association.save(!autosave)
|
335
|
+
saved = association.save(:validate => !autosave)
|
357
336
|
raise ActiveRecord::Rollback if !saved && autosave
|
358
337
|
saved
|
359
338
|
end
|
@@ -376,7 +355,7 @@ module ActiveRecord
|
|
376
355
|
if autosave && association.marked_for_destruction?
|
377
356
|
association.destroy
|
378
357
|
elsif autosave != false
|
379
|
-
saved = association.save(!autosave) if association.new_record? || autosave
|
358
|
+
saved = association.save(:validate => !autosave) if association.new_record? || autosave
|
380
359
|
|
381
360
|
if association.updated?
|
382
361
|
association_id = association.send(reflection.options[:primary_key] || :id)
|
@@ -392,4 +371,4 @@ module ActiveRecord
|
|
392
371
|
end
|
393
372
|
end
|
394
373
|
end
|
395
|
-
end
|
374
|
+
end
|