activerecord 2.3.18 → 3.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +105 -34
- data/examples/performance.rb +3 -39
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +81 -47
- data/lib/active_record/aggregations.rb +1 -3
- data/lib/active_record/association_preload.rb +39 -54
- data/lib/active_record/associations.rb +262 -419
- data/lib/active_record/associations/association_collection.rb +85 -100
- data/lib/active_record/associations/association_proxy.rb +20 -18
- data/lib/active_record/associations/belongs_to_association.rb +8 -8
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +13 -35
- data/lib/active_record/associations/has_many_association.rb +11 -19
- data/lib/active_record/associations/has_many_through_association.rb +30 -183
- data/lib/active_record/associations/has_one_association.rb +10 -10
- data/lib/active_record/associations/has_one_through_association.rb +13 -11
- data/lib/active_record/associations/through_association_scope.rb +153 -0
- data/lib/active_record/attribute_methods.rb +17 -370
- data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
- data/lib/active_record/attribute_methods/dirty.rb +87 -0
- data/lib/active_record/attribute_methods/primary_key.rb +44 -0
- data/lib/active_record/attribute_methods/query.rb +37 -0
- data/lib/active_record/attribute_methods/read.rb +116 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +60 -0
- data/lib/active_record/attribute_methods/write.rb +37 -0
- data/lib/active_record/autosave_association.rb +20 -41
- data/lib/active_record/base.rb +357 -1180
- data/lib/active_record/batches.rb +10 -16
- data/lib/active_record/callbacks.rb +66 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +17 -13
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +5 -25
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -72
- data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -49
- data/lib/active_record/connection_adapters/mysql_adapter.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +84 -46
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +9 -3
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +34 -65
- data/lib/active_record/fixtures.rb +21 -25
- data/lib/active_record/locale/en.yml +9 -27
- data/lib/active_record/locking/optimistic.rb +16 -48
- data/lib/active_record/migration.rb +59 -46
- data/lib/active_record/named_scope.rb +85 -92
- data/lib/active_record/nested_attributes.rb +18 -24
- data/lib/active_record/observer.rb +18 -94
- data/lib/active_record/railtie.rb +83 -0
- data/lib/active_record/railties/controller_runtime.rb +38 -0
- data/lib/active_record/railties/databases.rake +477 -0
- data/lib/active_record/railties/subscriber.rb +27 -0
- data/lib/active_record/reflection.rb +2 -15
- data/lib/active_record/relation.rb +339 -0
- data/lib/active_record/relation/calculations.rb +259 -0
- data/lib/active_record/relation/finder_methods.rb +315 -0
- data/lib/active_record/relation/predicate_builder.rb +46 -0
- data/lib/active_record/relation/query_methods.rb +218 -0
- data/lib/active_record/relation/spawn_methods.rb +102 -0
- data/lib/active_record/schema_dumper.rb +10 -6
- data/lib/active_record/serialization.rb +31 -74
- data/lib/active_record/serializers/xml_serializer.rb +33 -121
- data/lib/active_record/session_store.rb +1 -9
- data/lib/active_record/test_case.rb +1 -3
- data/lib/active_record/timestamp.rb +7 -5
- data/lib/active_record/transactions.rb +9 -9
- data/lib/active_record/validations.rb +51 -1100
- data/lib/active_record/validations/associated.rb +47 -0
- data/lib/active_record/validations/uniqueness.rb +181 -0
- data/lib/active_record/version.rb +3 -3
- data/lib/generators/active_record.rb +30 -0
- data/lib/generators/active_record/migration/migration_generator.rb +25 -0
- data/lib/generators/active_record/migration/templates/migration.rb +11 -0
- data/lib/generators/active_record/model/model_generator.rb +33 -0
- data/lib/generators/active_record/model/templates/migration.rb +16 -0
- data/lib/generators/active_record/model/templates/model.rb +5 -0
- data/lib/generators/active_record/observer/observer_generator.rb +15 -0
- data/lib/generators/active_record/observer/templates/observer.rb +2 -0
- data/lib/generators/active_record/session_migration/session_migration_generator.rb +24 -0
- data/lib/generators/active_record/session_migration/templates/migration.rb +16 -0
- metadata +67 -325
- data/RUNNING_UNIT_TESTS +0 -36
- data/Rakefile +0 -268
- data/install.rb +0 -30
- data/lib/active_record/calculations.rb +0 -321
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -57
- data/lib/active_record/dirty.rb +0 -183
- data/lib/active_record/serializers/json_serializer.rb +0 -91
- data/lib/activerecord.rb +0 -2
- data/test/assets/example.log +0 -1
- data/test/assets/flowers.jpg +0 -0
- data/test/cases/aaa_create_tables_test.rb +0 -24
- data/test/cases/active_schema_test_mysql.rb +0 -122
- data/test/cases/active_schema_test_postgresql.rb +0 -24
- data/test/cases/adapter_test.rb +0 -144
- data/test/cases/aggregations_test.rb +0 -167
- data/test/cases/ar_schema_test.rb +0 -32
- data/test/cases/associations/belongs_to_associations_test.rb +0 -438
- data/test/cases/associations/callbacks_test.rb +0 -161
- data/test/cases/associations/cascaded_eager_loading_test.rb +0 -131
- data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +0 -36
- data/test/cases/associations/eager_load_nested_include_test.rb +0 -131
- data/test/cases/associations/eager_load_nested_polymorphic_include.rb +0 -19
- data/test/cases/associations/eager_singularization_test.rb +0 -145
- data/test/cases/associations/eager_test.rb +0 -852
- data/test/cases/associations/extension_test.rb +0 -62
- data/test/cases/associations/habtm_join_table_test.rb +0 -56
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +0 -827
- data/test/cases/associations/has_many_associations_test.rb +0 -1273
- data/test/cases/associations/has_many_through_associations_test.rb +0 -360
- data/test/cases/associations/has_one_associations_test.rb +0 -330
- data/test/cases/associations/has_one_through_associations_test.rb +0 -209
- data/test/cases/associations/inner_join_association_test.rb +0 -93
- data/test/cases/associations/inverse_associations_test.rb +0 -566
- data/test/cases/associations/join_model_test.rb +0 -712
- data/test/cases/associations_test.rb +0 -282
- data/test/cases/attribute_methods_test.rb +0 -305
- data/test/cases/autosave_association_test.rb +0 -1218
- data/test/cases/base_test.rb +0 -2166
- data/test/cases/batches_test.rb +0 -81
- data/test/cases/binary_test.rb +0 -30
- data/test/cases/calculations_test.rb +0 -360
- data/test/cases/callbacks_observers_test.rb +0 -38
- data/test/cases/callbacks_test.rb +0 -438
- data/test/cases/class_inheritable_attributes_test.rb +0 -32
- data/test/cases/column_alias_test.rb +0 -17
- data/test/cases/column_definition_test.rb +0 -70
- data/test/cases/connection_pool_test.rb +0 -25
- data/test/cases/connection_test_firebird.rb +0 -8
- data/test/cases/connection_test_mysql.rb +0 -65
- data/test/cases/copy_table_test_sqlite.rb +0 -80
- data/test/cases/counter_cache_test.rb +0 -84
- data/test/cases/database_statements_test.rb +0 -12
- data/test/cases/datatype_test_postgresql.rb +0 -204
- data/test/cases/date_time_test.rb +0 -37
- data/test/cases/default_test_firebird.rb +0 -16
- data/test/cases/defaults_test.rb +0 -111
- data/test/cases/deprecated_finder_test.rb +0 -30
- data/test/cases/dirty_test.rb +0 -316
- data/test/cases/finder_respond_to_test.rb +0 -76
- data/test/cases/finder_test.rb +0 -1098
- data/test/cases/fixtures_test.rb +0 -661
- data/test/cases/helper.rb +0 -68
- data/test/cases/i18n_test.rb +0 -46
- data/test/cases/inheritance_test.rb +0 -262
- data/test/cases/invalid_date_test.rb +0 -24
- data/test/cases/json_serialization_test.rb +0 -219
- data/test/cases/lifecycle_test.rb +0 -193
- data/test/cases/locking_test.rb +0 -350
- data/test/cases/method_scoping_test.rb +0 -704
- data/test/cases/migration_test.rb +0 -1649
- data/test/cases/migration_test_firebird.rb +0 -124
- data/test/cases/mixin_test.rb +0 -96
- data/test/cases/modules_test.rb +0 -109
- data/test/cases/multiple_db_test.rb +0 -85
- data/test/cases/named_scope_test.rb +0 -372
- data/test/cases/nested_attributes_test.rb +0 -840
- data/test/cases/pk_test.rb +0 -119
- data/test/cases/pooled_connections_test.rb +0 -103
- data/test/cases/query_cache_test.rb +0 -129
- data/test/cases/readonly_test.rb +0 -107
- data/test/cases/reflection_test.rb +0 -234
- data/test/cases/reload_models_test.rb +0 -22
- data/test/cases/repair_helper.rb +0 -50
- data/test/cases/reserved_word_test_mysql.rb +0 -176
- data/test/cases/sanitize_test.rb +0 -25
- data/test/cases/schema_authorization_test_postgresql.rb +0 -75
- data/test/cases/schema_dumper_test.rb +0 -211
- data/test/cases/schema_test_postgresql.rb +0 -178
- data/test/cases/serialization_test.rb +0 -47
- data/test/cases/sp_test_mysql.rb +0 -16
- data/test/cases/synonym_test_oracle.rb +0 -17
- data/test/cases/timestamp_test.rb +0 -75
- data/test/cases/transactions_test.rb +0 -543
- data/test/cases/unconnected_test.rb +0 -32
- data/test/cases/validations_i18n_test.rb +0 -925
- data/test/cases/validations_test.rb +0 -1684
- data/test/cases/xml_serialization_test.rb +0 -240
- data/test/cases/yaml_serialization_test.rb +0 -11
- data/test/config.rb +0 -5
- data/test/connections/jdbc_jdbcderby/connection.rb +0 -18
- data/test/connections/jdbc_jdbch2/connection.rb +0 -18
- data/test/connections/jdbc_jdbchsqldb/connection.rb +0 -18
- data/test/connections/jdbc_jdbcmysql/connection.rb +0 -26
- data/test/connections/jdbc_jdbcpostgresql/connection.rb +0 -26
- data/test/connections/jdbc_jdbcsqlite3/connection.rb +0 -25
- data/test/connections/native_db2/connection.rb +0 -25
- data/test/connections/native_firebird/connection.rb +0 -26
- data/test/connections/native_frontbase/connection.rb +0 -27
- data/test/connections/native_mysql/connection.rb +0 -25
- data/test/connections/native_openbase/connection.rb +0 -21
- data/test/connections/native_oracle/connection.rb +0 -27
- data/test/connections/native_postgresql/connection.rb +0 -21
- data/test/connections/native_sqlite/connection.rb +0 -25
- data/test/connections/native_sqlite3/connection.rb +0 -25
- data/test/connections/native_sqlite3/in_memory_connection.rb +0 -18
- data/test/connections/native_sybase/connection.rb +0 -23
- data/test/fixtures/accounts.yml +0 -29
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author_addresses.yml +0 -5
- data/test/fixtures/author_favorites.yml +0 -4
- data/test/fixtures/authors.yml +0 -9
- data/test/fixtures/binaries.yml +0 -132
- data/test/fixtures/books.yml +0 -7
- data/test/fixtures/categories.yml +0 -14
- data/test/fixtures/categories/special_categories.yml +0 -9
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +0 -4
- data/test/fixtures/categories_ordered.yml +0 -7
- data/test/fixtures/categories_posts.yml +0 -23
- data/test/fixtures/categorizations.yml +0 -17
- data/test/fixtures/clubs.yml +0 -6
- data/test/fixtures/comments.yml +0 -59
- data/test/fixtures/companies.yml +0 -56
- data/test/fixtures/computers.yml +0 -4
- data/test/fixtures/courses.yml +0 -7
- data/test/fixtures/customers.yml +0 -26
- data/test/fixtures/developers.yml +0 -21
- data/test/fixtures/developers_projects.yml +0 -17
- data/test/fixtures/edges.yml +0 -6
- data/test/fixtures/entrants.yml +0 -14
- data/test/fixtures/faces.yml +0 -11
- data/test/fixtures/fk_test_has_fk.yml +0 -3
- data/test/fixtures/fk_test_has_pk.yml +0 -2
- data/test/fixtures/funny_jokes.yml +0 -10
- data/test/fixtures/interests.yml +0 -33
- data/test/fixtures/items.yml +0 -4
- data/test/fixtures/jobs.yml +0 -7
- data/test/fixtures/legacy_things.yml +0 -3
- data/test/fixtures/mateys.yml +0 -4
- data/test/fixtures/member_types.yml +0 -6
- data/test/fixtures/members.yml +0 -6
- data/test/fixtures/memberships.yml +0 -20
- data/test/fixtures/men.yml +0 -5
- data/test/fixtures/minimalistics.yml +0 -2
- data/test/fixtures/mixed_case_monkeys.yml +0 -6
- data/test/fixtures/mixins.yml +0 -29
- data/test/fixtures/movies.yml +0 -7
- data/test/fixtures/naked/csv/accounts.csv +0 -1
- data/test/fixtures/naked/yml/accounts.yml +0 -1
- data/test/fixtures/naked/yml/companies.yml +0 -1
- data/test/fixtures/naked/yml/courses.yml +0 -1
- data/test/fixtures/organizations.yml +0 -5
- data/test/fixtures/owners.yml +0 -7
- data/test/fixtures/parrots.yml +0 -27
- data/test/fixtures/parrots_pirates.yml +0 -7
- data/test/fixtures/people.yml +0 -15
- data/test/fixtures/pets.yml +0 -14
- data/test/fixtures/pirates.yml +0 -9
- data/test/fixtures/polymorphic_designs.yml +0 -19
- data/test/fixtures/polymorphic_prices.yml +0 -19
- data/test/fixtures/posts.yml +0 -52
- data/test/fixtures/price_estimates.yml +0 -7
- data/test/fixtures/projects.yml +0 -7
- data/test/fixtures/readers.yml +0 -9
- data/test/fixtures/references.yml +0 -17
- data/test/fixtures/reserved_words/distinct.yml +0 -5
- data/test/fixtures/reserved_words/distincts_selects.yml +0 -11
- data/test/fixtures/reserved_words/group.yml +0 -14
- data/test/fixtures/reserved_words/select.yml +0 -8
- data/test/fixtures/reserved_words/values.yml +0 -7
- data/test/fixtures/ships.yml +0 -5
- data/test/fixtures/sponsors.yml +0 -9
- data/test/fixtures/subscribers.yml +0 -7
- data/test/fixtures/subscriptions.yml +0 -12
- data/test/fixtures/taggings.yml +0 -28
- data/test/fixtures/tags.yml +0 -7
- data/test/fixtures/tasks.yml +0 -7
- data/test/fixtures/tees.yml +0 -4
- data/test/fixtures/ties.yml +0 -4
- data/test/fixtures/topics.yml +0 -42
- data/test/fixtures/toys.yml +0 -4
- data/test/fixtures/treasures.yml +0 -10
- data/test/fixtures/vertices.yml +0 -4
- data/test/fixtures/warehouse-things.yml +0 -3
- data/test/fixtures/zines.yml +0 -5
- data/test/migrations/broken/100_migration_that_raises_exception.rb +0 -10
- data/test/migrations/decimal/1_give_me_big_numbers.rb +0 -15
- data/test/migrations/duplicate/1_people_have_last_names.rb +0 -9
- data/test/migrations/duplicate/2_we_need_reminders.rb +0 -12
- data/test/migrations/duplicate/3_foo.rb +0 -7
- data/test/migrations/duplicate/3_innocent_jointable.rb +0 -12
- data/test/migrations/duplicate_names/20080507052938_chunky.rb +0 -7
- data/test/migrations/duplicate_names/20080507053028_chunky.rb +0 -7
- data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +0 -12
- data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +0 -9
- data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +0 -12
- data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +0 -9
- data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +0 -8
- data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +0 -12
- data/test/migrations/missing/1000_people_have_middle_names.rb +0 -9
- data/test/migrations/missing/1_people_have_last_names.rb +0 -9
- data/test/migrations/missing/3_we_need_reminders.rb +0 -12
- data/test/migrations/missing/4_innocent_jointable.rb +0 -12
- data/test/migrations/valid/1_people_have_last_names.rb +0 -9
- data/test/migrations/valid/2_we_need_reminders.rb +0 -12
- data/test/migrations/valid/3_innocent_jointable.rb +0 -12
- data/test/models/author.rb +0 -151
- data/test/models/auto_id.rb +0 -4
- data/test/models/binary.rb +0 -2
- data/test/models/bird.rb +0 -9
- data/test/models/book.rb +0 -4
- data/test/models/categorization.rb +0 -5
- data/test/models/category.rb +0 -34
- data/test/models/citation.rb +0 -6
- data/test/models/club.rb +0 -13
- data/test/models/column_name.rb +0 -3
- data/test/models/comment.rb +0 -29
- data/test/models/company.rb +0 -173
- data/test/models/company_in_module.rb +0 -78
- data/test/models/computer.rb +0 -3
- data/test/models/contact.rb +0 -16
- data/test/models/contract.rb +0 -5
- data/test/models/course.rb +0 -3
- data/test/models/customer.rb +0 -73
- data/test/models/default.rb +0 -2
- data/test/models/developer.rb +0 -101
- data/test/models/edge.rb +0 -5
- data/test/models/entrant.rb +0 -3
- data/test/models/essay.rb +0 -3
- data/test/models/event.rb +0 -3
- data/test/models/event_author.rb +0 -8
- data/test/models/face.rb +0 -7
- data/test/models/guid.rb +0 -2
- data/test/models/interest.rb +0 -5
- data/test/models/invoice.rb +0 -4
- data/test/models/item.rb +0 -7
- data/test/models/job.rb +0 -5
- data/test/models/joke.rb +0 -3
- data/test/models/keyboard.rb +0 -3
- data/test/models/legacy_thing.rb +0 -3
- data/test/models/line_item.rb +0 -3
- data/test/models/man.rb +0 -9
- data/test/models/matey.rb +0 -4
- data/test/models/member.rb +0 -12
- data/test/models/member_detail.rb +0 -5
- data/test/models/member_type.rb +0 -3
- data/test/models/membership.rb +0 -9
- data/test/models/minimalistic.rb +0 -2
- data/test/models/mixed_case_monkey.rb +0 -3
- data/test/models/movie.rb +0 -5
- data/test/models/order.rb +0 -4
- data/test/models/organization.rb +0 -6
- data/test/models/owner.rb +0 -5
- data/test/models/parrot.rb +0 -22
- data/test/models/person.rb +0 -16
- data/test/models/pet.rb +0 -5
- data/test/models/pirate.rb +0 -80
- data/test/models/polymorphic_design.rb +0 -3
- data/test/models/polymorphic_price.rb +0 -3
- data/test/models/post.rb +0 -102
- data/test/models/price_estimate.rb +0 -3
- data/test/models/project.rb +0 -30
- data/test/models/reader.rb +0 -4
- data/test/models/reference.rb +0 -4
- data/test/models/reply.rb +0 -46
- data/test/models/ship.rb +0 -19
- data/test/models/ship_part.rb +0 -7
- data/test/models/sponsor.rb +0 -4
- data/test/models/subject.rb +0 -4
- data/test/models/subscriber.rb +0 -8
- data/test/models/subscription.rb +0 -4
- data/test/models/tag.rb +0 -7
- data/test/models/tagging.rb +0 -10
- data/test/models/task.rb +0 -3
- data/test/models/tee.rb +0 -4
- data/test/models/tie.rb +0 -4
- data/test/models/topic.rb +0 -80
- data/test/models/toy.rb +0 -6
- data/test/models/treasure.rb +0 -8
- data/test/models/vertex.rb +0 -9
- data/test/models/warehouse_thing.rb +0 -5
- data/test/models/zine.rb +0 -3
- data/test/schema/mysql_specific_schema.rb +0 -31
- data/test/schema/postgresql_specific_schema.rb +0 -114
- data/test/schema/schema.rb +0 -550
- data/test/schema/schema2.rb +0 -6
- data/test/schema/sqlite_specific_schema.rb +0 -25
@@ -1,98 +1,15 @@
|
|
1
|
+
require 'active_support/core_ext/enumerable'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module AttributeMethods #:nodoc:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
def self.included(base)
|
7
|
-
base.extend ClassMethods
|
8
|
-
base.attribute_method_suffix(*DEFAULT_SUFFIXES)
|
9
|
-
base.cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
|
10
|
-
base.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
|
11
|
-
base.cattr_accessor :time_zone_aware_attributes, :instance_writer => false
|
12
|
-
base.time_zone_aware_attributes = false
|
13
|
-
base.class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
|
14
|
-
base.skip_time_zone_conversion_for_attributes = []
|
15
|
-
end
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include ActiveModel::AttributeMethods
|
16
7
|
|
17
|
-
# Declare and check for suffixed attribute methods.
|
18
8
|
module ClassMethods
|
19
|
-
# Declares a method available for all attributes with the given suffix.
|
20
|
-
# Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method
|
21
|
-
#
|
22
|
-
# #{attr}#{suffix}(*args, &block)
|
23
|
-
#
|
24
|
-
# to
|
25
|
-
#
|
26
|
-
# attribute#{suffix}(#{attr}, *args, &block)
|
27
|
-
#
|
28
|
-
# An <tt>attribute#{suffix}</tt> instance method must exist and accept at least
|
29
|
-
# the +attr+ argument.
|
30
|
-
#
|
31
|
-
# For example:
|
32
|
-
#
|
33
|
-
# class Person < ActiveRecord::Base
|
34
|
-
# attribute_method_suffix '_changed?'
|
35
|
-
#
|
36
|
-
# private
|
37
|
-
# def attribute_changed?(attr)
|
38
|
-
# ...
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# person = Person.find(1)
|
43
|
-
# person.name_changed? # => false
|
44
|
-
# person.name = 'Hubert'
|
45
|
-
# person.name_changed? # => true
|
46
|
-
def attribute_method_suffix(*suffixes)
|
47
|
-
attribute_method_suffixes.concat suffixes
|
48
|
-
rebuild_attribute_method_regexp
|
49
|
-
end
|
50
|
-
|
51
|
-
# Returns MatchData if method_name is an attribute method.
|
52
|
-
def match_attribute_method?(method_name)
|
53
|
-
rebuild_attribute_method_regexp unless defined?(@@attribute_method_regexp) && @@attribute_method_regexp
|
54
|
-
@@attribute_method_regexp.match(method_name)
|
55
|
-
end
|
56
|
-
|
57
|
-
|
58
|
-
# Contains the names of the generated attribute methods.
|
59
|
-
def generated_methods #:nodoc:
|
60
|
-
@generated_methods ||= Set.new
|
61
|
-
end
|
62
|
-
|
63
|
-
def generated_methods?
|
64
|
-
!generated_methods.empty?
|
65
|
-
end
|
66
|
-
|
67
9
|
# Generates all the attribute related methods for columns in the database
|
68
10
|
# accessors, mutators and query methods.
|
69
11
|
def define_attribute_methods
|
70
|
-
|
71
|
-
columns_hash.each do |name, column|
|
72
|
-
unless instance_method_already_implemented?(name)
|
73
|
-
if self.serialized_attributes[name]
|
74
|
-
define_read_method_for_serialized_attribute(name)
|
75
|
-
elsif create_time_zone_conversion_attribute?(name, column)
|
76
|
-
define_read_method_for_time_zone_conversion(name)
|
77
|
-
else
|
78
|
-
define_read_method(name.to_sym, name, column)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
unless instance_method_already_implemented?("#{name}=")
|
83
|
-
if self.serialized_attributes[name]
|
84
|
-
define_write_method_for_serialized_attribute(name)
|
85
|
-
elsif create_time_zone_conversion_attribute?(name, column)
|
86
|
-
define_write_method_for_time_zone_conversion(name)
|
87
|
-
else
|
88
|
-
define_write_method(name.to_sym)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
unless instance_method_already_implemented?("#{name}?")
|
93
|
-
define_question_method(name)
|
94
|
-
end
|
95
|
-
end
|
12
|
+
super(columns_hash.keys)
|
96
13
|
end
|
97
14
|
|
98
15
|
# Checks whether the method is defined in the model or any of its subclasses
|
@@ -100,304 +17,34 @@ module ActiveRecord
|
|
100
17
|
# method is defined by Active Record though.
|
101
18
|
def instance_method_already_implemented?(method_name)
|
102
19
|
method_name = method_name.to_s
|
103
|
-
|
104
|
-
|
105
|
-
@@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map(&:to_s).to_set
|
20
|
+
@_defined_class_methods ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map {|m| m.to_s }.to_set
|
21
|
+
@@_defined_activerecord_methods ||= (ActiveRecord::Base.public_instance_methods(false) | ActiveRecord::Base.private_instance_methods(false) | ActiveRecord::Base.protected_instance_methods(false)).map{|m| m.to_s }.to_set
|
106
22
|
raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
|
107
23
|
@_defined_class_methods.include?(method_name)
|
108
24
|
end
|
109
|
-
|
110
|
-
alias :define_read_methods :define_attribute_methods
|
111
|
-
|
112
|
-
# +cache_attributes+ allows you to declare which converted attribute values should
|
113
|
-
# be cached. Usually caching only pays off for attributes with expensive conversion
|
114
|
-
# methods, like time related columns (e.g. +created_at+, +updated_at+).
|
115
|
-
def cache_attributes(*attribute_names)
|
116
|
-
attribute_names.each {|attr| cached_attributes << attr.to_s}
|
117
|
-
end
|
118
|
-
|
119
|
-
# Returns the attributes which are cached. By default time related columns
|
120
|
-
# with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
|
121
|
-
def cached_attributes
|
122
|
-
@cached_attributes ||=
|
123
|
-
columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map(&:name).to_set
|
124
|
-
end
|
125
|
-
|
126
|
-
# Returns +true+ if the provided attribute is being cached.
|
127
|
-
def cache_attribute?(attr_name)
|
128
|
-
cached_attributes.include?(attr_name)
|
129
|
-
end
|
130
|
-
|
131
|
-
private
|
132
|
-
# Suffixes a, ?, c become regexp /(a|\?|c)$/
|
133
|
-
def rebuild_attribute_method_regexp
|
134
|
-
suffixes = attribute_method_suffixes.map { |s| Regexp.escape(s) }
|
135
|
-
@@attribute_method_regexp = /(#{suffixes.join('|')})\z/.freeze
|
136
|
-
end
|
137
|
-
|
138
|
-
# Default to =, ?, _before_type_cast
|
139
|
-
def attribute_method_suffixes
|
140
|
-
@@attribute_method_suffixes ||= []
|
141
|
-
end
|
142
|
-
|
143
|
-
def create_time_zone_conversion_attribute?(name, column)
|
144
|
-
time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type)
|
145
|
-
end
|
146
|
-
|
147
|
-
# Define an attribute reader method. Cope with nil column.
|
148
|
-
def define_read_method(symbol, attr_name, column)
|
149
|
-
cast_code = column.type_cast_code('v') if column
|
150
|
-
access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
|
151
|
-
|
152
|
-
unless attr_name.to_s == self.primary_key.to_s
|
153
|
-
access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
|
154
|
-
end
|
155
|
-
|
156
|
-
if cache_attribute?(attr_name)
|
157
|
-
access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
|
158
|
-
end
|
159
|
-
evaluate_attribute_method attr_name, "def #{symbol}; #{access_code}; end"
|
160
|
-
end
|
161
|
-
|
162
|
-
# Define read method for serialized attribute.
|
163
|
-
def define_read_method_for_serialized_attribute(attr_name)
|
164
|
-
evaluate_attribute_method attr_name, "def #{attr_name}; unserialize_attribute('#{attr_name}'); end"
|
165
|
-
end
|
166
|
-
|
167
|
-
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
|
168
|
-
# This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
|
169
|
-
def define_read_method_for_time_zone_conversion(attr_name)
|
170
|
-
method_body = <<-EOV
|
171
|
-
def #{attr_name}(reload = false)
|
172
|
-
cached = @attributes_cache['#{attr_name}']
|
173
|
-
return cached if cached && !reload
|
174
|
-
time = read_attribute('#{attr_name}')
|
175
|
-
@attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
|
176
|
-
end
|
177
|
-
EOV
|
178
|
-
evaluate_attribute_method attr_name, method_body
|
179
|
-
end
|
180
|
-
|
181
|
-
# Defines a predicate method <tt>attr_name?</tt>.
|
182
|
-
def define_question_method(attr_name)
|
183
|
-
evaluate_attribute_method attr_name, "def #{attr_name}?; query_attribute('#{attr_name}'); end", "#{attr_name}?"
|
184
|
-
end
|
185
|
-
|
186
|
-
def define_write_method(attr_name)
|
187
|
-
evaluate_attribute_method attr_name, "def #{attr_name}=(new_value);write_attribute('#{attr_name}', new_value);end", "#{attr_name}="
|
188
|
-
end
|
189
|
-
|
190
|
-
# Defined for all serialized attributes. Disallows assigning already serialized YAML.
|
191
|
-
def define_write_method_for_serialized_attribute(attr_name)
|
192
|
-
method_body = <<-EOV
|
193
|
-
def #{attr_name}=(value)
|
194
|
-
if value.is_a?(String) and value =~ /^---/
|
195
|
-
raise ActiveRecordError, "You tried to assign already serialized content to #{attr_name}. This is disabled due to security issues."
|
196
|
-
end
|
197
|
-
write_attribute(:#{attr_name}, value)
|
198
|
-
end
|
199
|
-
EOV
|
200
|
-
evaluate_attribute_method attr_name, method_body, "#{attr_name}="
|
201
|
-
end
|
202
|
-
|
203
|
-
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
|
204
|
-
# This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
|
205
|
-
def define_write_method_for_time_zone_conversion(attr_name)
|
206
|
-
method_body = <<-EOV
|
207
|
-
def #{attr_name}=(time)
|
208
|
-
unless time.acts_like?(:time)
|
209
|
-
time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
|
210
|
-
end
|
211
|
-
time = time.in_time_zone rescue nil if time
|
212
|
-
write_attribute(:#{attr_name}, time)
|
213
|
-
end
|
214
|
-
EOV
|
215
|
-
evaluate_attribute_method attr_name, method_body, "#{attr_name}="
|
216
|
-
end
|
217
|
-
|
218
|
-
# Evaluate the definition for an attribute related method
|
219
|
-
def evaluate_attribute_method(attr_name, method_definition, method_name=attr_name)
|
220
|
-
|
221
|
-
unless method_name.to_s == primary_key.to_s
|
222
|
-
generated_methods << method_name
|
223
|
-
end
|
224
|
-
|
225
|
-
begin
|
226
|
-
class_eval(method_definition, __FILE__)
|
227
|
-
rescue SyntaxError => err
|
228
|
-
generated_methods.delete(attr_name)
|
229
|
-
if logger
|
230
|
-
logger.warn "Exception occurred during reader method compilation."
|
231
|
-
logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
|
232
|
-
logger.warn err.message
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end # ClassMethods
|
237
|
-
|
25
|
+
end
|
238
26
|
|
239
|
-
# Allows access to the object attributes, which are held in the <tt>@attributes</tt> hash, as though they
|
240
|
-
# were first-class methods. So a Person class with a name attribute can use Person#name and
|
241
|
-
# Person#name= and never directly use the attributes hash -- except for multiple assigns with
|
242
|
-
# ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
|
243
|
-
# the completed attribute is not +nil+ or 0.
|
244
|
-
#
|
245
|
-
# It's also possible to instantiate related objects, so a Client class belonging to the clients
|
246
|
-
# table with a +master_id+ foreign key can instantiate master through Client#master.
|
247
27
|
def method_missing(method_id, *args, &block)
|
248
|
-
method_name = method_id.to_s
|
249
|
-
|
250
|
-
if self.class.private_method_defined?(method_name)
|
251
|
-
raise NoMethodError.new("Attempt to call private method", method_name, args)
|
252
|
-
end
|
253
|
-
|
254
28
|
# If we haven't generated any methods yet, generate them, then
|
255
29
|
# see if we've created the method we're looking for.
|
256
|
-
if !self.class.
|
30
|
+
if !self.class.attribute_methods_generated?
|
257
31
|
self.class.define_attribute_methods
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
end
|
262
|
-
|
263
|
-
if self.class.primary_key.to_s == method_name
|
264
|
-
id
|
265
|
-
elsif md = self.class.match_attribute_method?(method_name)
|
266
|
-
attribute_name, method_type = md.pre_match, md.to_s
|
267
|
-
if @attributes.include?(attribute_name)
|
268
|
-
__send__("attribute#{method_type}", attribute_name, *args, &block)
|
269
|
-
else
|
270
|
-
super
|
271
|
-
end
|
272
|
-
elsif @attributes.include?(method_name)
|
273
|
-
read_attribute(method_name)
|
32
|
+
method_name = method_id.to_s
|
33
|
+
guard_private_attribute_method!(method_name, args)
|
34
|
+
send(method_id, *args, &block)
|
274
35
|
else
|
275
36
|
super
|
276
37
|
end
|
277
38
|
end
|
278
39
|
|
279
|
-
|
280
|
-
|
281
|
-
def read_attribute(attr_name)
|
282
|
-
attr_name = attr_name.to_s
|
283
|
-
if !(value = @attributes[attr_name]).nil?
|
284
|
-
if column = column_for_attribute(attr_name)
|
285
|
-
if unserializable_attribute?(attr_name, column)
|
286
|
-
unserialize_attribute(attr_name)
|
287
|
-
else
|
288
|
-
column.type_cast(value)
|
289
|
-
end
|
290
|
-
else
|
291
|
-
value
|
292
|
-
end
|
293
|
-
else
|
294
|
-
nil
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
def read_attribute_before_type_cast(attr_name)
|
299
|
-
@attributes[attr_name]
|
300
|
-
end
|
301
|
-
|
302
|
-
# Returns true if the attribute is of a text column and marked for serialization.
|
303
|
-
def unserializable_attribute?(attr_name, column)
|
304
|
-
column.text? && self.class.serialized_attributes[attr_name]
|
305
|
-
end
|
306
|
-
|
307
|
-
# Returns the unserialized object of the attribute.
|
308
|
-
def unserialize_attribute(attr_name)
|
309
|
-
unserialized_object = object_from_yaml(@attributes[attr_name])
|
310
|
-
|
311
|
-
if unserialized_object.is_a?(self.class.serialized_attributes[attr_name]) || unserialized_object.nil?
|
312
|
-
@attributes.frozen? ? unserialized_object : @attributes[attr_name] = unserialized_object
|
313
|
-
else
|
314
|
-
raise SerializationTypeMismatch,
|
315
|
-
"#{attr_name} was supposed to be a #{self.class.serialized_attributes[attr_name]}, but was a #{unserialized_object.class.to_s}"
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
|
320
|
-
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings for fixnum and float
|
321
|
-
# columns are turned into +nil+.
|
322
|
-
def write_attribute(attr_name, value)
|
323
|
-
attr_name = attr_name.to_s
|
324
|
-
@attributes_cache.delete(attr_name)
|
325
|
-
if (column = column_for_attribute(attr_name)) && column.number?
|
326
|
-
@attributes[attr_name] = convert_number_column_value(value)
|
327
|
-
else
|
328
|
-
@attributes[attr_name] = value
|
329
|
-
end
|
330
|
-
end
|
331
|
-
|
332
|
-
|
333
|
-
def query_attribute(attr_name)
|
334
|
-
unless value = read_attribute(attr_name)
|
335
|
-
false
|
336
|
-
else
|
337
|
-
column = self.class.columns_hash[attr_name]
|
338
|
-
if column.nil?
|
339
|
-
if Numeric === value || value !~ /[^0-9]/
|
340
|
-
!value.to_i.zero?
|
341
|
-
else
|
342
|
-
return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
|
343
|
-
!value.blank?
|
344
|
-
end
|
345
|
-
elsif column.number?
|
346
|
-
!value.zero?
|
347
|
-
else
|
348
|
-
!value.blank?
|
349
|
-
end
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
|
354
|
-
# <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
|
355
|
-
# which will all return +true+.
|
356
|
-
alias :respond_to_without_attributes? :respond_to?
|
357
|
-
def respond_to?(method, include_private_methods = false)
|
358
|
-
method_name = method.to_s
|
359
|
-
if super
|
360
|
-
return true
|
361
|
-
elsif !include_private_methods && super(method, true)
|
362
|
-
# If we're here than we haven't found among non-private methods
|
363
|
-
# but found among all methods. Which means that given method is private.
|
364
|
-
return false
|
365
|
-
elsif !self.class.generated_methods?
|
366
|
-
self.class.define_attribute_methods
|
367
|
-
if self.class.generated_methods.include?(method_name)
|
368
|
-
return true
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
if @attributes.nil?
|
373
|
-
return super
|
374
|
-
elsif @attributes.include?(method_name)
|
375
|
-
return true
|
376
|
-
elsif md = self.class.match_attribute_method?(method_name)
|
377
|
-
return true if @attributes.include?(md.pre_match)
|
378
|
-
end
|
40
|
+
def respond_to?(*args)
|
41
|
+
self.class.define_attribute_methods
|
379
42
|
super
|
380
43
|
end
|
381
44
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
raise ActiveRecord::MissingAttributeError, "missing attribute: #{attr_name}", stack
|
386
|
-
end
|
387
|
-
|
388
|
-
# Handle *? for method_missing.
|
389
|
-
def attribute?(attribute_name)
|
390
|
-
query_attribute(attribute_name)
|
391
|
-
end
|
392
|
-
|
393
|
-
# Handle *= for method_missing.
|
394
|
-
def attribute=(attribute_name, value)
|
395
|
-
write_attribute(attribute_name, value)
|
396
|
-
end
|
397
|
-
|
398
|
-
# Handle *_before_type_cast for method_missing.
|
399
|
-
def attribute_before_type_cast(attribute_name)
|
400
|
-
read_attribute_before_type_cast(attribute_name)
|
45
|
+
protected
|
46
|
+
def attribute_method?(attr_name)
|
47
|
+
attr_name == 'id' || attributes.include?(attr_name)
|
401
48
|
end
|
402
49
|
end
|
403
50
|
end
|
@@ -0,0 +1,33 @@
|
|
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
|
+
self.attribute_names.inject({}) do |attrs, name|
|
17
|
+
attrs[name] = read_attribute_before_type_cast(name)
|
18
|
+
attrs
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
# Handle *_before_type_cast for method_missing.
|
24
|
+
def attribute_before_type_cast(attribute_name)
|
25
|
+
if attribute_name == 'id'
|
26
|
+
read_attribute_before_type_cast(self.class.primary_key)
|
27
|
+
else
|
28
|
+
read_attribute_before_type_cast(attribute_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|