activerecord 1.0.0 → 2.0.0
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 +4928 -3
- data/README +45 -46
- data/RUNNING_UNIT_TESTS +8 -11
- data/Rakefile +247 -0
- data/install.rb +8 -38
- data/lib/active_record/aggregations.rb +64 -49
- data/lib/active_record/associations/association_collection.rb +217 -47
- data/lib/active_record/associations/association_proxy.rb +159 -0
- data/lib/active_record/associations/belongs_to_association.rb +56 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +155 -37
- data/lib/active_record/associations/has_many_association.rb +145 -75
- data/lib/active_record/associations/has_many_through_association.rb +283 -0
- data/lib/active_record/associations/has_one_association.rb +96 -0
- data/lib/active_record/associations.rb +1537 -304
- data/lib/active_record/attribute_methods.rb +328 -0
- data/lib/active_record/base.rb +2001 -588
- data/lib/active_record/calculations.rb +269 -0
- data/lib/active_record/callbacks.rb +169 -165
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +308 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +472 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +306 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +125 -279
- data/lib/active_record/connection_adapters/mysql_adapter.rb +442 -77
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +805 -135
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +353 -69
- data/lib/active_record/fixtures.rb +946 -100
- data/lib/active_record/locking/optimistic.rb +144 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +417 -0
- data/lib/active_record/observer.rb +142 -32
- data/lib/active_record/query_cache.rb +23 -0
- data/lib/active_record/reflection.rb +163 -70
- data/lib/active_record/schema.rb +58 -0
- data/lib/active_record/schema_dumper.rb +171 -0
- data/lib/active_record/serialization.rb +98 -0
- data/lib/active_record/serializers/json_serializer.rb +71 -0
- data/lib/active_record/serializers/xml_serializer.rb +315 -0
- data/lib/active_record/timestamp.rb +41 -0
- data/lib/active_record/transactions.rb +87 -57
- data/lib/active_record/validations.rb +909 -122
- data/lib/active_record/vendor/db2.rb +362 -0
- data/lib/active_record/vendor/mysql.rb +126 -29
- data/lib/active_record/version.rb +9 -0
- data/lib/active_record.rb +35 -7
- data/lib/activerecord.rb +1 -0
- data/test/aaa_create_tables_test.rb +72 -0
- data/test/abstract_unit.rb +73 -5
- data/test/active_schema_test_mysql.rb +43 -0
- data/test/adapter_test.rb +105 -0
- data/test/adapter_test_sqlserver.rb +95 -0
- data/test/aggregations_test.rb +110 -16
- data/test/all.sh +2 -2
- data/test/ar_schema_test.rb +33 -0
- data/test/association_inheritance_reload.rb +14 -0
- data/test/associations/ar_joins_test.rb +0 -0
- data/test/associations/callbacks_test.rb +147 -0
- data/test/associations/cascaded_eager_loading_test.rb +110 -0
- data/test/associations/eager_singularization_test.rb +145 -0
- data/test/associations/eager_test.rb +442 -0
- data/test/associations/extension_test.rb +47 -0
- data/test/associations/inner_join_association_test.rb +88 -0
- data/test/associations/join_model_test.rb +553 -0
- data/test/associations_test.rb +1930 -267
- data/test/attribute_methods_test.rb +146 -0
- data/test/base_test.rb +1316 -84
- data/test/binary_test.rb +32 -0
- data/test/calculations_test.rb +251 -0
- data/test/callbacks_test.rb +400 -0
- data/test/class_inheritable_attributes_test.rb +3 -4
- data/test/column_alias_test.rb +17 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connection_test_mysql.rb +30 -0
- data/test/connections/native_db2/connection.rb +25 -0
- data/test/connections/native_firebird/connection.rb +26 -0
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +21 -18
- data/test/connections/native_openbase/connection.rb +21 -0
- data/test/connections/native_oracle/connection.rb +27 -0
- data/test/connections/native_postgresql/connection.rb +17 -18
- data/test/connections/native_sqlite/connection.rb +17 -16
- data/test/connections/native_sqlite3/connection.rb +25 -0
- data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
- data/test/connections/native_sybase/connection.rb +23 -0
- data/test/copy_table_test_sqlite.rb +69 -0
- data/test/datatype_test_postgresql.rb +203 -0
- data/test/date_time_test.rb +37 -0
- data/test/default_test_firebird.rb +16 -0
- data/test/defaults_test.rb +67 -0
- data/test/deprecated_finder_test.rb +30 -0
- data/test/finder_test.rb +607 -32
- data/test/fixtures/accounts.yml +28 -0
- 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.rb +107 -0
- data/test/fixtures/author_favorites.yml +4 -0
- data/test/fixtures/authors.yml +7 -0
- data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
- data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
- data/test/fixtures/bad_fixtures/blank_line +3 -0
- data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
- data/test/fixtures/bad_fixtures/missing_value +1 -0
- data/test/fixtures/binaries.yml +132 -0
- data/test/fixtures/binary.rb +2 -0
- data/test/fixtures/book.rb +4 -0
- data/test/fixtures/books.yml +7 -0
- data/test/fixtures/categories/special_categories.yml +9 -0
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
- data/test/fixtures/categories.yml +14 -0
- data/test/fixtures/categories_ordered.yml +7 -0
- data/test/fixtures/categories_posts.yml +23 -0
- data/test/fixtures/categorization.rb +5 -0
- data/test/fixtures/categorizations.yml +17 -0
- data/test/fixtures/category.rb +26 -0
- data/test/fixtures/citation.rb +6 -0
- data/test/fixtures/comment.rb +23 -0
- data/test/fixtures/comments.yml +59 -0
- data/test/fixtures/companies.yml +55 -0
- data/test/fixtures/company.rb +81 -4
- data/test/fixtures/company_in_module.rb +32 -6
- data/test/fixtures/computer.rb +4 -0
- data/test/fixtures/computers.yml +4 -0
- data/test/fixtures/contact.rb +16 -0
- data/test/fixtures/courses.yml +7 -0
- data/test/fixtures/customer.rb +28 -3
- data/test/fixtures/customers.yml +17 -0
- data/test/fixtures/db_definitions/db2.drop.sql +33 -0
- data/test/fixtures/db_definitions/db2.sql +235 -0
- data/test/fixtures/db_definitions/db22.drop.sql +2 -0
- data/test/fixtures/db_definitions/db22.sql +5 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +65 -0
- data/test/fixtures/db_definitions/firebird.sql +310 -0
- data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
- data/test/fixtures/db_definitions/firebird2.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +33 -0
- data/test/fixtures/db_definitions/frontbase.sql +273 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase.sql +318 -0
- data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase2.sql +7 -0
- data/test/fixtures/db_definitions/oracle.drop.sql +67 -0
- data/test/fixtures/db_definitions/oracle.sql +330 -0
- data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle2.sql +6 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +44 -0
- data/test/fixtures/db_definitions/postgresql.sql +217 -38
- data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/postgresql2.sql +2 -2
- data/test/fixtures/db_definitions/schema.rb +354 -0
- data/test/fixtures/db_definitions/schema2.rb +11 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +33 -0
- data/test/fixtures/db_definitions/sqlite.sql +139 -5
- data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlite2.sql +1 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
- data/test/fixtures/db_definitions/sybase.sql +222 -0
- data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
- data/test/fixtures/db_definitions/sybase2.sql +5 -0
- data/test/fixtures/developer.rb +70 -6
- data/test/fixtures/developers.yml +21 -0
- data/test/fixtures/developers_projects/david_action_controller +2 -1
- data/test/fixtures/developers_projects/david_active_record +2 -1
- data/test/fixtures/developers_projects.yml +17 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/entrants.yml +14 -0
- data/test/fixtures/example.log +1 -0
- data/test/fixtures/fk_test_has_fk.yml +3 -0
- data/test/fixtures/fk_test_has_pk.yml +2 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/funny_jokes.yml +10 -0
- data/test/fixtures/item.rb +7 -0
- data/test/fixtures/items.yml +4 -0
- data/test/fixtures/joke.rb +3 -0
- data/test/fixtures/keyboard.rb +3 -0
- data/test/fixtures/legacy_thing.rb +3 -0
- data/test/fixtures/legacy_things.yml +3 -0
- data/test/fixtures/matey.rb +4 -0
- data/test/fixtures/mateys.yml +4 -0
- data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
- data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/minimalistic.rb +2 -0
- data/test/fixtures/minimalistics.yml +2 -0
- data/test/fixtures/mixed_case_monkey.rb +3 -0
- data/test/fixtures/mixed_case_monkeys.yml +6 -0
- data/test/fixtures/mixins.yml +29 -0
- data/test/fixtures/movies.yml +7 -0
- data/test/fixtures/naked/csv/accounts.csv +1 -0
- data/test/fixtures/naked/yml/accounts.yml +1 -0
- data/test/fixtures/naked/yml/companies.yml +1 -0
- data/test/fixtures/naked/yml/courses.yml +1 -0
- data/test/fixtures/order.rb +4 -0
- data/test/fixtures/parrot.rb +13 -0
- data/test/fixtures/parrots.yml +27 -0
- data/test/fixtures/parrots_pirates.yml +7 -0
- data/test/fixtures/people.yml +3 -0
- data/test/fixtures/person.rb +4 -0
- data/test/fixtures/pirate.rb +5 -0
- data/test/fixtures/pirates.yml +9 -0
- data/test/fixtures/post.rb +59 -0
- data/test/fixtures/posts.yml +48 -0
- data/test/fixtures/project.rb +27 -2
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/reader.rb +4 -0
- data/test/fixtures/readers.yml +4 -0
- data/test/fixtures/reply.rb +18 -2
- data/test/fixtures/reserved_words/distinct.yml +5 -0
- data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
- data/test/fixtures/reserved_words/group.yml +14 -0
- data/test/fixtures/reserved_words/select.yml +8 -0
- data/test/fixtures/reserved_words/values.yml +7 -0
- data/test/fixtures/ship.rb +3 -0
- data/test/fixtures/ships.yml +5 -0
- data/test/fixtures/subject.rb +4 -0
- data/test/fixtures/subscriber.rb +4 -3
- data/test/fixtures/tag.rb +7 -0
- data/test/fixtures/tagging.rb +10 -0
- data/test/fixtures/taggings.yml +25 -0
- data/test/fixtures/tags.yml +7 -0
- data/test/fixtures/task.rb +3 -0
- data/test/fixtures/tasks.yml +7 -0
- data/test/fixtures/topic.rb +20 -3
- data/test/fixtures/topics.yml +22 -0
- data/test/fixtures/treasure.rb +4 -0
- data/test/fixtures/treasures.yml +10 -0
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +574 -8
- data/test/inheritance_test.rb +113 -27
- data/test/json_serialization_test.rb +180 -0
- data/test/lifecycle_test.rb +56 -29
- data/test/locking_test.rb +273 -0
- data/test/method_scoping_test.rb +416 -0
- data/test/migration_test.rb +933 -0
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_test.rb +95 -0
- data/test/modules_test.rb +23 -10
- data/test/multiple_db_test.rb +17 -3
- data/test/pk_test.rb +59 -15
- data/test/query_cache_test.rb +104 -0
- data/test/readonly_test.rb +107 -0
- data/test/reflection_test.rb +124 -27
- data/test/reserved_word_test_mysql.rb +177 -0
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +131 -0
- data/test/schema_test_postgresql.rb +64 -0
- data/test/serialization_test.rb +47 -0
- data/test/synonym_test_oracle.rb +17 -0
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +48 -0
- data/test/transactions_test.rb +227 -29
- data/test/unconnected_test.rb +14 -6
- data/test/validations_test.rb +1293 -32
- data/test/xml_serialization_test.rb +202 -0
- metadata +347 -143
- data/dev-utils/eval_debugger.rb +0 -9
- data/examples/associations.rb +0 -87
- data/examples/shared_setup.rb +0 -15
- data/examples/validation.rb +0 -88
- data/lib/active_record/deprecated_associations.rb +0 -70
- data/lib/active_record/support/class_attribute_accessors.rb +0 -43
- data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
- data/lib/active_record/support/clean_logger.rb +0 -10
- data/lib/active_record/support/inflector.rb +0 -70
- data/lib/active_record/vendor/simple.rb +0 -702
- data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
- data/lib/active_record/wrappings.rb +0 -59
- data/rakefile +0 -122
- data/test/deprecated_associations_test.rb +0 -336
- data/test/fixtures/accounts/signals37 +0 -3
- data/test/fixtures/accounts/unknown +0 -2
- data/test/fixtures/companies/first_client +0 -6
- data/test/fixtures/companies/first_firm +0 -4
- data/test/fixtures/companies/second_client +0 -6
- data/test/fixtures/courses/java +0 -2
- data/test/fixtures/courses/ruby +0 -2
- data/test/fixtures/customers/david +0 -6
- data/test/fixtures/db_definitions/mysql.sql +0 -96
- data/test/fixtures/db_definitions/mysql2.sql +0 -4
- data/test/fixtures/developers/david +0 -2
- data/test/fixtures/developers/jamis +0 -2
- data/test/fixtures/entrants/first +0 -3
- data/test/fixtures/entrants/second +0 -3
- data/test/fixtures/entrants/third +0 -3
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/movies/first +0 -2
- data/test/fixtures/movies/second +0 -2
- data/test/fixtures/projects/action_controller +0 -2
- data/test/fixtures/projects/active_record +0 -2
- data/test/fixtures/topics/first +0 -9
- data/test/fixtures/topics/second +0 -8
- data/test/inflector_test.rb +0 -104
- data/test/thread_safety_test.rb +0 -33
@@ -1,32 +1,32 @@
|
|
1
1
|
require 'observer'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
-
# Callbacks are hooks into the lifecycle of an Active Record object that
|
5
|
-
# before or after an alteration of the object state. This can be used to make sure that associated and
|
6
|
-
# dependent objects are deleted when destroy is called (by overwriting before_destroy) or to massage attributes
|
7
|
-
# before they're validated (by overwriting before_validation). As an example of the callbacks initiated, consider
|
8
|
-
# the Base#save call:
|
9
|
-
#
|
10
|
-
# * (-) save
|
11
|
-
# * (-) valid
|
12
|
-
# * (1) before_validation
|
13
|
-
# * (2) before_validation_on_create
|
14
|
-
# * (-) validate
|
15
|
-
# * (-) validate_on_create
|
16
|
-
# * (
|
17
|
-
# * (
|
18
|
-
# * (
|
19
|
-
# * (
|
20
|
-
# * (-) create
|
21
|
-
# * (
|
22
|
-
# * (
|
23
|
-
#
|
24
|
-
# That's a total of
|
25
|
-
# Active Record
|
4
|
+
# Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic
|
5
|
+
# before or after an alteration of the object state. This can be used to make sure that associated and
|
6
|
+
# dependent objects are deleted when destroy is called (by overwriting +before_destroy+) or to massage attributes
|
7
|
+
# before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
|
8
|
+
# the <tt>Base#save</tt> call:
|
9
|
+
#
|
10
|
+
# * (-) <tt>save</tt>
|
11
|
+
# * (-) <tt>valid</tt>
|
12
|
+
# * (1) <tt>before_validation</tt>
|
13
|
+
# * (2) <tt>before_validation_on_create</tt>
|
14
|
+
# * (-) <tt>validate</tt>
|
15
|
+
# * (-) <tt>validate_on_create</tt>
|
16
|
+
# * (3) <tt>after_validation</tt>
|
17
|
+
# * (4) <tt>after_validation_on_create</tt>
|
18
|
+
# * (5) <tt>before_save</tt>
|
19
|
+
# * (6) <tt>before_create</tt>
|
20
|
+
# * (-) <tt>create</tt>
|
21
|
+
# * (7) <tt>after_create</tt>
|
22
|
+
# * (8) <tt>after_save</tt>
|
23
|
+
#
|
24
|
+
# That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the
|
25
|
+
# Active Record lifecycle.
|
26
26
|
#
|
27
27
|
# Examples:
|
28
28
|
# class CreditCard < ActiveRecord::Base
|
29
|
-
# # Strip everything but digits, so the user can specify "555 234 34" or
|
29
|
+
# # Strip everything but digits, so the user can specify "555 234 34" or
|
30
30
|
# # "5552-3434" or both will mean "55523434"
|
31
31
|
# def before_validation_on_create
|
32
32
|
# self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
|
@@ -34,18 +34,19 @@ module ActiveRecord
|
|
34
34
|
# end
|
35
35
|
#
|
36
36
|
# class Subscription < ActiveRecord::Base
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
37
|
+
# before_create :record_signup
|
38
|
+
#
|
39
|
+
# private
|
40
|
+
# def record_signup
|
41
|
+
# self.signed_up_on = Date.today
|
42
|
+
# end
|
41
43
|
# end
|
42
44
|
#
|
43
45
|
# class Firm < ActiveRecord::Base
|
44
46
|
# # Destroys the associated clients and people when the firm is destroyed
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# end
|
47
|
+
# before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
|
48
|
+
# before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
|
49
|
+
# end
|
49
50
|
#
|
50
51
|
# == Inheritable callback queues
|
51
52
|
#
|
@@ -61,8 +62,8 @@ module ActiveRecord
|
|
61
62
|
# before_destroy :destroy_readers
|
62
63
|
# end
|
63
64
|
#
|
64
|
-
# Now, when Topic#destroy is run only +destroy_author+ is called. When Reply#destroy is run both +destroy_author+ and
|
65
|
-
# +destroy_readers+
|
65
|
+
# Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is run, both +destroy_author+ and
|
66
|
+
# +destroy_readers+ are called. Contrast this to the situation where we've implemented the save behavior through overwriteable
|
66
67
|
# methods:
|
67
68
|
#
|
68
69
|
# class Topic < ActiveRecord::Base
|
@@ -73,15 +74,19 @@ module ActiveRecord
|
|
73
74
|
# def before_destroy() destroy_readers end
|
74
75
|
# end
|
75
76
|
#
|
76
|
-
# In that case, Reply#destroy would only run +destroy_readers+ and _not_ +destroy_author+. So use the callback macros when
|
77
|
-
# you want to ensure that a certain callback is called for the entire hierarchy and the regular overwriteable methods
|
78
|
-
# want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
|
77
|
+
# In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+. So, use the callback macros when
|
78
|
+
# you want to ensure that a certain callback is called for the entire hierarchy, and use the regular overwriteable methods
|
79
|
+
# when you want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
|
80
|
+
#
|
81
|
+
# *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the
|
82
|
+
# associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won't
|
83
|
+
# be inherited.
|
79
84
|
#
|
80
85
|
# == Types of callbacks
|
81
86
|
#
|
82
|
-
# There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
|
87
|
+
# There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
|
83
88
|
# inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects are the
|
84
|
-
# recommended approaches, inline methods using a proc
|
89
|
+
# recommended approaches, inline methods using a proc are sometimes appropriate (such as for creating mix-ins), and inline
|
85
90
|
# eval methods are deprecated.
|
86
91
|
#
|
87
92
|
# The method reference callbacks work by specifying a protected or private method available in the object, like this:
|
@@ -115,8 +120,8 @@ module ActiveRecord
|
|
115
120
|
# def after_save(record)
|
116
121
|
# record.credit_card_number = decrypt(record.credit_card_number)
|
117
122
|
# end
|
118
|
-
#
|
119
|
-
# alias_method :
|
123
|
+
#
|
124
|
+
# alias_method :after_find, :after_save
|
120
125
|
#
|
121
126
|
# private
|
122
127
|
# def encrypt(value)
|
@@ -124,7 +129,7 @@ module ActiveRecord
|
|
124
129
|
# end
|
125
130
|
#
|
126
131
|
# def decrypt(value)
|
127
|
-
# # Secrecy is
|
132
|
+
# # Secrecy is unveiled
|
128
133
|
# end
|
129
134
|
# end
|
130
135
|
#
|
@@ -138,200 +143,199 @@ module ActiveRecord
|
|
138
143
|
# before_destroy 'self.class.delete_all "parent_id = #{id}"'
|
139
144
|
# end
|
140
145
|
#
|
141
|
-
# Notice that single
|
146
|
+
# Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback is triggered. Also note that these
|
142
147
|
# inline callbacks can be stacked just like the regular ones:
|
143
148
|
#
|
144
149
|
# class Topic < ActiveRecord::Base
|
145
|
-
# before_destroy 'self.class.delete_all "parent_id = #{id}"',
|
150
|
+
# before_destroy 'self.class.delete_all "parent_id = #{id}"',
|
146
151
|
# 'puts "Evaluated after parents are destroyed"'
|
147
152
|
# end
|
148
153
|
#
|
149
|
-
# == The after_find and after_initialize exceptions
|
154
|
+
# == The +after_find+ and +after_initialize+ exceptions
|
150
155
|
#
|
151
|
-
# Because after_find and after_initialize
|
152
|
-
# to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, after_find and
|
153
|
-
# after_initialize
|
154
|
-
#
|
156
|
+
# Because +after_find+ and +after_initialize+ are called for each object found and instantiated by a finder, such as <tt>Base.find(:all)</tt>, we've had
|
157
|
+
# to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, +after_find+ and
|
158
|
+
# +after_initialize+ will only be run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
|
159
|
+
# callback types will be called.
|
160
|
+
#
|
161
|
+
# == <tt>before_validation*</tt> returning statements
|
162
|
+
#
|
163
|
+
# If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be aborted and <tt>Base#save</tt> will return +false+.
|
164
|
+
# If <tt>Base#save!</tt> is called it will raise a +RecordNotSaved+ exception.
|
165
|
+
# Nothing will be appended to the errors object.
|
166
|
+
#
|
167
|
+
# == Canceling callbacks
|
168
|
+
#
|
169
|
+
# If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are cancelled. If an <tt>after_*</tt> callback returns
|
170
|
+
# +false+, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks
|
171
|
+
# defined as methods on the model, which are called last.
|
155
172
|
module Callbacks
|
156
|
-
CALLBACKS = %w(
|
157
|
-
after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
|
173
|
+
CALLBACKS = %w(
|
174
|
+
after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
|
158
175
|
after_validation before_validation_on_create after_validation_on_create before_validation_on_update
|
159
176
|
after_validation_on_update before_destroy after_destroy
|
160
177
|
)
|
161
178
|
|
162
|
-
def self.
|
163
|
-
|
179
|
+
def self.included(base) #:nodoc:
|
180
|
+
base.extend Observable
|
164
181
|
|
165
|
-
|
166
|
-
|
167
|
-
class << self
|
168
|
-
include Observable
|
169
|
-
alias_method :instantiate_without_callbacks, :instantiate
|
170
|
-
alias_method :instantiate, :instantiate_with_callbacks
|
171
|
-
end
|
182
|
+
[:create_or_update, :valid?, :create, :update, :destroy].each do |method|
|
183
|
+
base.send :alias_method_chain, method, :callbacks
|
172
184
|
end
|
173
185
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
alias_method :valid_without_callbacks, :valid?
|
182
|
-
alias_method :valid?, :valid_with_callbacks
|
183
|
-
|
184
|
-
alias_method :create_without_callbacks, :create
|
185
|
-
alias_method :create, :create_with_callbacks
|
186
|
-
|
187
|
-
alias_method :update_without_callbacks, :update
|
188
|
-
alias_method :update, :update_with_callbacks
|
189
|
-
|
190
|
-
alias_method :destroy_without_callbacks, :destroy
|
191
|
-
alias_method :destroy, :destroy_with_callbacks
|
186
|
+
CALLBACKS.each do |method|
|
187
|
+
base.class_eval <<-"end_eval"
|
188
|
+
def self.#{method}(*callbacks, &block)
|
189
|
+
callbacks << block if block_given?
|
190
|
+
write_inheritable_array(#{method.to_sym.inspect}, callbacks)
|
191
|
+
end
|
192
|
+
end_eval
|
192
193
|
end
|
193
|
-
|
194
|
-
CALLBACKS.each { |cb| base.class_eval("def self.#{cb}(*methods) write_inheritable_array(\"#{cb}\", methods) end") }
|
195
194
|
end
|
196
195
|
|
197
|
-
|
198
|
-
|
199
|
-
object = instantiate_without_callbacks(record)
|
200
|
-
object.callback(:after_find) if object.respond_to_without_attributes?(:after_find)
|
201
|
-
object.callback(:after_initialize) if object.respond_to_without_attributes?(:after_initialize)
|
202
|
-
object
|
203
|
-
end
|
204
|
-
end
|
196
|
+
# Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
|
197
|
+
#def after_find() end
|
205
198
|
|
206
|
-
# Is called
|
207
|
-
#
|
199
|
+
# Is called after the object has been instantiated by a call to <tt>Base.new</tt>.
|
200
|
+
#def after_initialize() end
|
208
201
|
|
209
|
-
# Is called
|
210
|
-
# def after_initialize() end
|
211
|
-
def initialize_with_callbacks(attributes = nil) #:nodoc:
|
212
|
-
initialize_without_callbacks(attributes)
|
213
|
-
yield self if block_given?
|
214
|
-
after_initialize if respond_to_without_attributes?(:after_initialize)
|
215
|
-
end
|
216
|
-
|
217
|
-
# Is called _before_ Base.save (regardless of whether it's a create or update save).
|
202
|
+
# Is called _before_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
|
218
203
|
def before_save() end
|
219
204
|
|
220
|
-
# Is called _after_ Base.save (regardless of whether it's a create or update save).
|
205
|
+
# Is called _after_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
|
206
|
+
#
|
207
|
+
# class Contact < ActiveRecord::Base
|
208
|
+
# after_save { logger.info( 'New contact saved!' ) }
|
209
|
+
# end
|
221
210
|
def after_save() end
|
222
211
|
def create_or_update_with_callbacks #:nodoc:
|
223
|
-
callback(:before_save)
|
224
|
-
create_or_update_without_callbacks
|
212
|
+
return false if callback(:before_save) == false
|
213
|
+
result = create_or_update_without_callbacks
|
225
214
|
callback(:after_save)
|
215
|
+
result
|
226
216
|
end
|
217
|
+
private :create_or_update_with_callbacks
|
227
218
|
|
228
|
-
# Is called _before_ Base.save on new objects that haven't been saved yet (no record exists).
|
219
|
+
# Is called _before_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
|
229
220
|
def before_create() end
|
230
221
|
|
231
|
-
# Is called _after_ Base.save on new objects that haven't been saved yet (no record exists).
|
222
|
+
# Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
|
232
223
|
def after_create() end
|
233
224
|
def create_with_callbacks #:nodoc:
|
234
|
-
callback(:before_create)
|
235
|
-
create_without_callbacks
|
225
|
+
return false if callback(:before_create) == false
|
226
|
+
result = create_without_callbacks
|
236
227
|
callback(:after_create)
|
228
|
+
result
|
237
229
|
end
|
230
|
+
private :create_with_callbacks
|
238
231
|
|
239
|
-
# Is called _before_ Base.save on existing objects that
|
232
|
+
# Is called _before_ <tt>Base.save</tt> on existing objects that have a record.
|
240
233
|
def before_update() end
|
241
234
|
|
242
|
-
# Is called _after_ Base.save on existing objects that
|
235
|
+
# Is called _after_ <tt>Base.save</tt> on existing objects that have a record.
|
243
236
|
def after_update() end
|
244
237
|
|
245
238
|
def update_with_callbacks #:nodoc:
|
246
|
-
callback(:before_update)
|
247
|
-
update_without_callbacks
|
239
|
+
return false if callback(:before_update) == false
|
240
|
+
result = update_without_callbacks
|
248
241
|
callback(:after_update)
|
242
|
+
result
|
249
243
|
end
|
244
|
+
private :update_with_callbacks
|
250
245
|
|
251
|
-
# Is called _before_ Validations.validate (which is part of the Base.save call).
|
246
|
+
# Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
|
252
247
|
def before_validation() end
|
253
248
|
|
254
|
-
# Is called _after_ Validations.validate (which is part of the Base.save call).
|
249
|
+
# Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
|
255
250
|
def after_validation() end
|
256
251
|
|
257
|
-
# Is called _before_ Validations.validate (which is part of the Base.save call) on new objects
|
252
|
+
# Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
|
258
253
|
# that haven't been saved yet (no record exists).
|
259
254
|
def before_validation_on_create() end
|
260
255
|
|
261
|
-
# Is called _after_ Validations.validate (which is part of the Base.save call) on new objects
|
256
|
+
# Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
|
262
257
|
# that haven't been saved yet (no record exists).
|
263
258
|
def after_validation_on_create() end
|
264
259
|
|
265
|
-
# Is called _before_ Validations.validate (which is part of the Base.save call) on
|
266
|
-
# existing objects that
|
260
|
+
# Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
|
261
|
+
# existing objects that have a record.
|
267
262
|
def before_validation_on_update() end
|
268
263
|
|
269
|
-
# Is called _after_ Validations.validate (which is part of the Base.save call) on
|
270
|
-
# existing objects that
|
264
|
+
# Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
|
265
|
+
# existing objects that have a record.
|
271
266
|
def after_validation_on_update() end
|
272
267
|
|
273
|
-
def valid_with_callbacks #:nodoc:
|
274
|
-
callback(:before_validation)
|
275
|
-
if new_record? then callback(:before_validation_on_create) else callback(:before_validation_on_update) end
|
268
|
+
def valid_with_callbacks? #:nodoc:
|
269
|
+
return false if callback(:before_validation) == false
|
270
|
+
if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end
|
271
|
+
return false if result == false
|
276
272
|
|
277
|
-
result = valid_without_callbacks
|
273
|
+
result = valid_without_callbacks?
|
278
274
|
|
279
275
|
callback(:after_validation)
|
280
276
|
if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
|
281
|
-
|
277
|
+
|
282
278
|
return result
|
283
279
|
end
|
284
280
|
|
285
|
-
# Is called _before_ Base.destroy
|
281
|
+
# Is called _before_ <tt>Base.destroy</tt>.
|
282
|
+
#
|
283
|
+
# Note: If you need to _destroy_ or _nullify_ associated records first,
|
284
|
+
# use the <tt>:dependent</tt> option on your associations.
|
286
285
|
def before_destroy() end
|
287
286
|
|
288
|
-
# Is called _after_ Base.destroy (and all the attributes have been frozen).
|
287
|
+
# Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen).
|
288
|
+
#
|
289
|
+
# class Contact < ActiveRecord::Base
|
290
|
+
# after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) }
|
291
|
+
# end
|
289
292
|
def after_destroy() end
|
290
293
|
def destroy_with_callbacks #:nodoc:
|
291
|
-
callback(:before_destroy)
|
292
|
-
destroy_without_callbacks
|
294
|
+
return false if callback(:before_destroy) == false
|
295
|
+
result = destroy_without_callbacks
|
293
296
|
callback(:after_destroy)
|
297
|
+
result
|
294
298
|
end
|
295
299
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
ActiveRecordError,
|
317
|
-
"Filters need to be either a symbol, string (to be eval'ed), proc/method, or " +
|
318
|
-
"class implementing a static filter method"
|
319
|
-
)
|
300
|
+
private
|
301
|
+
def callback(method)
|
302
|
+
notify(method)
|
303
|
+
|
304
|
+
callbacks_for(method).each do |callback|
|
305
|
+
result = case callback
|
306
|
+
when Symbol
|
307
|
+
self.send(callback)
|
308
|
+
when String
|
309
|
+
eval(callback, binding)
|
310
|
+
when Proc, Method
|
311
|
+
callback.call(self)
|
312
|
+
else
|
313
|
+
if callback.respond_to?(method)
|
314
|
+
callback.send(method, self)
|
315
|
+
else
|
316
|
+
raise ActiveRecordError, "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method."
|
317
|
+
end
|
318
|
+
end
|
319
|
+
return false if result == false
|
320
320
|
end
|
321
|
+
|
322
|
+
result = send(method) if respond_to_without_attributes?(method)
|
323
|
+
|
324
|
+
return result
|
325
|
+
end
|
326
|
+
|
327
|
+
def callbacks_for(method)
|
328
|
+
self.class.read_inheritable_attribute(method.to_sym) or []
|
329
|
+
end
|
330
|
+
|
331
|
+
def invoke_and_notify(method)
|
332
|
+
notify(method)
|
333
|
+
send(method) if respond_to_without_attributes?(method)
|
334
|
+
end
|
335
|
+
|
336
|
+
def notify(method) #:nodoc:
|
337
|
+
self.class.changed
|
338
|
+
self.class.notify_observers(method, self)
|
321
339
|
end
|
322
|
-
end
|
323
|
-
|
324
|
-
def filter_block?(filter)
|
325
|
-
filter.respond_to?("call") && (filter.arity == 1 || filter.arity == -1)
|
326
|
-
end
|
327
|
-
|
328
|
-
def filter_class?(filter, callback_method)
|
329
|
-
filter.respond_to?(callback_method)
|
330
|
-
end
|
331
|
-
|
332
|
-
def notify(callback_method) #:nodoc:
|
333
|
-
self.class.changed
|
334
|
-
self.class.notify_observers(callback_method, self)
|
335
|
-
end
|
336
340
|
end
|
337
|
-
end
|
341
|
+
end
|