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,34 +1,48 @@
|
|
1
|
+
require 'active_support/core_ext/array'
|
2
|
+
require 'active_support/core_ext/hash/except'
|
3
|
+
require 'active_support/core_ext/object/metaclass'
|
4
|
+
|
1
5
|
module ActiveRecord
|
2
6
|
module NamedScope
|
3
|
-
|
4
|
-
# * <tt>scoped</tt> - which allows for the creation of anonymous \scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
|
5
|
-
#
|
6
|
-
# These anonymous \scopes tend to be useful when procedurally generating complex queries, where passing
|
7
|
-
# intermediate values (scopes) around as first-class objects is convenient.
|
8
|
-
#
|
9
|
-
# You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
|
10
|
-
def self.included(base)
|
11
|
-
base.extend ClassMethods
|
12
|
-
end
|
7
|
+
extend ActiveSupport::Concern
|
13
8
|
|
14
9
|
module ClassMethods
|
15
|
-
|
16
|
-
|
10
|
+
# Returns a relation if invoked without any arguments.
|
11
|
+
#
|
12
|
+
# posts = Post.scoped
|
13
|
+
# posts.size # Fires "select count(*) from posts" and returns the count
|
14
|
+
# posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
|
15
|
+
#
|
16
|
+
# Returns an anonymous named scope if any options are supplied.
|
17
|
+
#
|
18
|
+
# shirts = Shirt.scoped(:conditions => {:color => 'red'})
|
19
|
+
# shirts = shirts.scoped(:include => :washing_instructions)
|
20
|
+
#
|
21
|
+
# Anonymous \scopes tend to be useful when procedurally generating complex queries, where passing
|
22
|
+
# intermediate values (scopes) around as first-class objects is convenient.
|
23
|
+
#
|
24
|
+
# You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
|
25
|
+
def scoped(options = {}, &block)
|
26
|
+
if options.present?
|
27
|
+
Scope.init(self, options, &block)
|
28
|
+
else
|
29
|
+
current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.spawn
|
30
|
+
end
|
17
31
|
end
|
18
32
|
|
19
|
-
def
|
20
|
-
|
33
|
+
def scopes
|
34
|
+
read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
|
21
35
|
end
|
22
36
|
|
23
37
|
# Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
|
24
38
|
# such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
|
25
39
|
#
|
26
40
|
# class Shirt < ActiveRecord::Base
|
27
|
-
#
|
28
|
-
#
|
41
|
+
# scope :red, :conditions => {:color => 'red'}
|
42
|
+
# scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
|
29
43
|
# end
|
30
|
-
#
|
31
|
-
# The above calls to <tt>
|
44
|
+
#
|
45
|
+
# The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
|
32
46
|
# in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
|
33
47
|
#
|
34
48
|
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it resembles the association object
|
@@ -54,7 +68,7 @@ module ActiveRecord
|
|
54
68
|
# Named \scopes can also be procedural:
|
55
69
|
#
|
56
70
|
# class Shirt < ActiveRecord::Base
|
57
|
-
#
|
71
|
+
# scope :colored, lambda { |color|
|
58
72
|
# { :conditions => { :color => color } }
|
59
73
|
# }
|
60
74
|
# end
|
@@ -64,7 +78,7 @@ module ActiveRecord
|
|
64
78
|
# Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
|
65
79
|
#
|
66
80
|
# class Shirt < ActiveRecord::Base
|
67
|
-
#
|
81
|
+
# scope :red, :conditions => {:color => 'red'} do
|
68
82
|
# def dom_id
|
69
83
|
# 'red_shirts'
|
70
84
|
# end
|
@@ -76,122 +90,101 @@ module ActiveRecord
|
|
76
90
|
# <tt>proxy_options</tt> method on the proxy itself.
|
77
91
|
#
|
78
92
|
# class Shirt < ActiveRecord::Base
|
79
|
-
#
|
93
|
+
# scope :colored, lambda { |color|
|
80
94
|
# { :conditions => { :color => color } }
|
81
95
|
# }
|
82
96
|
# end
|
83
97
|
#
|
84
98
|
# expected_options = { :conditions => { :colored => 'red' } }
|
85
99
|
# assert_equal expected_options, Shirt.colored('red').proxy_options
|
86
|
-
def
|
100
|
+
def scope(name, options = {}, &block)
|
87
101
|
name = name.to_sym
|
88
102
|
|
103
|
+
if !scopes[name] && respond_to?(name, true)
|
104
|
+
raise ArgumentError, "Cannot define scope :#{name} because #{self.name}.#{name} method already exists."
|
105
|
+
end
|
106
|
+
|
89
107
|
scopes[name] = lambda do |parent_scope, *args|
|
90
|
-
Scope.
|
91
|
-
when Hash
|
108
|
+
Scope.init(parent_scope, case options
|
109
|
+
when Hash, Relation
|
92
110
|
options
|
93
111
|
when Proc
|
94
|
-
|
95
|
-
options.bind(parent_scope).call(*args)
|
96
|
-
else
|
97
|
-
options.call(*args)
|
98
|
-
end
|
112
|
+
options.call(*args)
|
99
113
|
end, &block)
|
100
114
|
end
|
101
|
-
|
102
|
-
|
103
|
-
|
115
|
+
metaclass.instance_eval do
|
116
|
+
define_method name do |*args|
|
117
|
+
scopes[name].call(self, *args)
|
118
|
+
end
|
104
119
|
end
|
105
120
|
end
|
106
|
-
end
|
107
121
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
[].methods.each do |m|
|
112
|
-
unless m =~ /^__/ || NON_DELEGATE_METHODS.include?(m.to_s)
|
113
|
-
delegate m, :to => :proxy_found
|
114
|
-
end
|
122
|
+
def named_scope(*args, &block)
|
123
|
+
ActiveSupport::Deprecation.warn("Base.named_scope has been deprecated, please use Base.scope instead", caller)
|
124
|
+
scope(*args, &block)
|
115
125
|
end
|
126
|
+
end
|
116
127
|
|
117
|
-
|
128
|
+
class Scope < Relation
|
129
|
+
attr_accessor :current_scoped_methods_when_defined
|
118
130
|
|
119
|
-
|
120
|
-
options ||= {}
|
121
|
-
[options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
|
122
|
-
extend Module.new(&block) if block_given?
|
123
|
-
unless (Scope === proxy_scope || ActiveRecord::Associations::AssociationCollection === proxy_scope)
|
124
|
-
@current_scoped_methods_when_defined = proxy_scope.send(:current_scoped_methods)
|
125
|
-
end
|
126
|
-
@proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
|
127
|
-
end
|
131
|
+
delegate :scopes, :with_scope, :with_exclusive_scope, :scoped_methods, :scoped, :to => :klass
|
128
132
|
|
129
|
-
def
|
130
|
-
|
131
|
-
end
|
133
|
+
def self.init(klass, options, &block)
|
134
|
+
relation = new(klass, klass.arel_table)
|
132
135
|
|
133
|
-
|
134
|
-
|
135
|
-
proxy_found.first(*args)
|
136
|
+
scope = if options.is_a?(Hash)
|
137
|
+
klass.scoped.apply_finder_options(options.except(:extend))
|
136
138
|
else
|
137
|
-
|
139
|
+
options ? klass.scoped.merge(options) : klass.scoped
|
138
140
|
end
|
139
|
-
end
|
140
141
|
|
141
|
-
|
142
|
-
if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
|
143
|
-
proxy_found.last(*args)
|
144
|
-
else
|
145
|
-
find(:last, *args)
|
146
|
-
end
|
147
|
-
end
|
142
|
+
relation = relation.merge(scope)
|
148
143
|
|
149
|
-
|
150
|
-
|
151
|
-
end
|
144
|
+
Array.wrap(options[:extend]).each {|extension| relation.send(:extend, extension) } if options.is_a?(Hash)
|
145
|
+
relation.send(:extend, Module.new(&block)) if block_given?
|
152
146
|
|
153
|
-
|
154
|
-
|
147
|
+
relation.current_scoped_methods_when_defined = klass.send(:current_scoped_methods)
|
148
|
+
relation
|
155
149
|
end
|
156
150
|
|
157
|
-
def
|
158
|
-
|
151
|
+
def first(*args)
|
152
|
+
if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
|
153
|
+
to_a.first(*args)
|
154
|
+
else
|
155
|
+
args.first.present? ? apply_finder_options(args.first).first : super
|
156
|
+
end
|
159
157
|
end
|
160
158
|
|
161
|
-
def
|
162
|
-
if
|
163
|
-
|
159
|
+
def last(*args)
|
160
|
+
if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
|
161
|
+
to_a.last(*args)
|
164
162
|
else
|
165
|
-
|
163
|
+
args.first.present? ? apply_finder_options(args.first).last : super
|
166
164
|
end
|
167
165
|
end
|
168
166
|
|
169
|
-
|
170
|
-
|
171
|
-
@found || load_found
|
167
|
+
def ==(other)
|
168
|
+
other.respond_to?(:to_ary) ? to_a == other.to_a : false
|
172
169
|
end
|
173
170
|
|
174
171
|
private
|
172
|
+
|
175
173
|
def method_missing(method, *args, &block)
|
176
|
-
if
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
method = :new if method == :build
|
181
|
-
if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined)
|
182
|
-
with_scope current_scoped_methods_when_defined do
|
183
|
-
proxy_scope.send(method, *args, &block)
|
184
|
-
end
|
174
|
+
if klass.respond_to?(method)
|
175
|
+
with_scope(self) do
|
176
|
+
if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined) && !scopes.include?(method)
|
177
|
+
with_scope(current_scoped_methods_when_defined) { klass.send(method, *args, &block) }
|
185
178
|
else
|
186
|
-
|
179
|
+
klass.send(method, *args, &block)
|
187
180
|
end
|
188
181
|
end
|
182
|
+
else
|
183
|
+
super
|
189
184
|
end
|
190
185
|
end
|
191
186
|
|
192
|
-
def load_found
|
193
|
-
@found = find(:all)
|
194
|
-
end
|
195
187
|
end
|
188
|
+
|
196
189
|
end
|
197
190
|
end
|
@@ -1,12 +1,16 @@
|
|
1
|
+
require 'active_support/core_ext/hash/except'
|
2
|
+
require 'active_support/core_ext/object/try'
|
3
|
+
|
1
4
|
module ActiveRecord
|
2
5
|
module NestedAttributes #:nodoc:
|
3
6
|
class TooManyRecords < ActiveRecordError
|
4
7
|
end
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
class_inheritable_accessor :nested_attributes_options, :instance_writer => false
|
13
|
+
self.nested_attributes_options = {}
|
10
14
|
end
|
11
15
|
|
12
16
|
# == Nested Attributes
|
@@ -45,14 +49,14 @@ module ActiveRecord
|
|
45
49
|
# create the member and avatar in one go:
|
46
50
|
#
|
47
51
|
# params = { :member => { :name => 'Jack', :avatar_attributes => { :icon => 'smiling' } } }
|
48
|
-
# member = Member.create(params)
|
52
|
+
# member = Member.create(params[:member])
|
49
53
|
# member.avatar.id # => 2
|
50
54
|
# member.avatar.icon # => 'smiling'
|
51
55
|
#
|
52
56
|
# It also allows you to update the avatar through the member:
|
53
57
|
#
|
54
|
-
# params = { :member
|
55
|
-
# member.update_attributes params[
|
58
|
+
# params = { :member => { :avatar_attributes => { :id => '2', :icon => 'sad' } } }
|
59
|
+
# member.update_attributes params[:member]
|
56
60
|
# member.avatar.icon # => 'sad'
|
57
61
|
#
|
58
62
|
# By default you will only be able to set and update attributes on the
|
@@ -71,7 +75,7 @@ module ActiveRecord
|
|
71
75
|
# member.avatar_attributes = { :id => '2', :_destroy => '1' }
|
72
76
|
# member.avatar.marked_for_destruction? # => true
|
73
77
|
# member.save
|
74
|
-
# member.avatar #=> nil
|
78
|
+
# member.reload.avatar #=> nil
|
75
79
|
#
|
76
80
|
# Note that the model will _not_ be destroyed until the parent is saved.
|
77
81
|
#
|
@@ -175,7 +179,7 @@ module ActiveRecord
|
|
175
179
|
# member.posts.detect { |p| p.id == 2 }.marked_for_destruction? # => true
|
176
180
|
# member.posts.length #=> 2
|
177
181
|
# member.save
|
178
|
-
# member.posts.length # => 1
|
182
|
+
# member.reload.posts.length # => 1
|
179
183
|
#
|
180
184
|
# === Saving
|
181
185
|
#
|
@@ -239,11 +243,11 @@ module ActiveRecord
|
|
239
243
|
# def pirate_attributes=(attributes)
|
240
244
|
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
|
241
245
|
# end
|
242
|
-
class_eval
|
246
|
+
class_eval %{
|
243
247
|
def #{association_name}_attributes=(attributes)
|
244
248
|
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
|
245
249
|
end
|
246
|
-
|
250
|
+
}, __FILE__, __LINE__
|
247
251
|
else
|
248
252
|
raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
|
249
253
|
end
|
@@ -315,7 +319,7 @@ module ActiveRecord
|
|
315
319
|
# })
|
316
320
|
#
|
317
321
|
# Will update the name of the Person with ID 1, build a new associated
|
318
|
-
# person with the name `John', and mark the
|
322
|
+
# person with the name `John', and mark the associated Person with ID 2
|
319
323
|
# for destruction.
|
320
324
|
#
|
321
325
|
# Also accepts an Array of attribute hashes:
|
@@ -340,24 +344,14 @@ module ActiveRecord
|
|
340
344
|
attributes_collection = attributes_collection.sort_by { |index, _| index.to_i }.map { |_, attributes| attributes }
|
341
345
|
end
|
342
346
|
|
343
|
-
association = send(association_name)
|
344
|
-
|
345
|
-
existing_records = if association.loaded?
|
346
|
-
association.to_a
|
347
|
-
else
|
348
|
-
attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
|
349
|
-
attribute_ids.present? ? association.all(:conditions => {association.primary_key => attribute_ids}) : []
|
350
|
-
end
|
351
|
-
|
352
347
|
attributes_collection.each do |attributes|
|
353
348
|
attributes = attributes.with_indifferent_access
|
354
349
|
|
355
350
|
if attributes['id'].blank?
|
356
351
|
unless reject_new_record?(association_name, attributes)
|
357
|
-
|
352
|
+
send(association_name).build(attributes.except(*UNASSIGNABLE_KEYS))
|
358
353
|
end
|
359
|
-
elsif existing_record =
|
360
|
-
association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
|
354
|
+
elsif existing_record = send(association_name).detect { |record| record.id.to_s == attributes['id'].to_s }
|
361
355
|
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
|
362
356
|
else
|
363
357
|
raise_nested_attributes_record_not_found(association_name, attributes['id'])
|
@@ -1,59 +1,6 @@
|
|
1
|
-
require '
|
2
|
-
require 'set'
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
3
2
|
|
4
3
|
module ActiveRecord
|
5
|
-
module Observing # :nodoc:
|
6
|
-
def self.included(base)
|
7
|
-
base.extend ClassMethods
|
8
|
-
end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
# Activates the observers assigned. Examples:
|
12
|
-
#
|
13
|
-
# # Calls PersonObserver.instance
|
14
|
-
# ActiveRecord::Base.observers = :person_observer
|
15
|
-
#
|
16
|
-
# # Calls Cacher.instance and GarbageCollector.instance
|
17
|
-
# ActiveRecord::Base.observers = :cacher, :garbage_collector
|
18
|
-
#
|
19
|
-
# # Same as above, just using explicit class references
|
20
|
-
# ActiveRecord::Base.observers = Cacher, GarbageCollector
|
21
|
-
#
|
22
|
-
# Note: Setting this does not instantiate the observers yet. +instantiate_observers+ is
|
23
|
-
# called during startup, and before each development request.
|
24
|
-
def observers=(*observers)
|
25
|
-
@observers = observers.flatten
|
26
|
-
end
|
27
|
-
|
28
|
-
# Gets the current observers.
|
29
|
-
def observers
|
30
|
-
@observers ||= []
|
31
|
-
end
|
32
|
-
|
33
|
-
# Instantiate the global Active Record observers.
|
34
|
-
def instantiate_observers
|
35
|
-
return if @observers.blank?
|
36
|
-
@observers.each do |observer|
|
37
|
-
if observer.respond_to?(:to_sym) # Symbol or String
|
38
|
-
observer.to_s.camelize.constantize.instance
|
39
|
-
elsif observer.respond_to?(:instance)
|
40
|
-
observer.instance
|
41
|
-
else
|
42
|
-
raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
protected
|
48
|
-
# Notify observers when the observed class is subclassed.
|
49
|
-
def inherited(subclass)
|
50
|
-
super
|
51
|
-
changed
|
52
|
-
notify_observers :observed_class_inherited, subclass
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
4
|
# Observer classes respond to lifecycle callbacks to implement trigger-like
|
58
5
|
# behavior outside the original class. This is a great way to reduce the
|
59
6
|
# clutter that normally comes when the model class is burdened with
|
@@ -139,58 +86,35 @@ module ActiveRecord
|
|
139
86
|
# load their observers by calling <tt>ModelObserver.instance</tt> before. Observers are
|
140
87
|
# singletons and that call instantiates and registers them.
|
141
88
|
#
|
142
|
-
class Observer
|
143
|
-
|
144
|
-
|
145
|
-
class << self
|
146
|
-
# Attaches the observer to the supplied model classes.
|
147
|
-
def observe(*models)
|
148
|
-
models.flatten!
|
149
|
-
models.collect! { |model| model.is_a?(Symbol) ? model.to_s.camelize.constantize : model }
|
150
|
-
define_method(:observed_classes) { Set.new(models) }
|
151
|
-
end
|
152
|
-
|
153
|
-
# The class observed by default is inferred from the observer's class name:
|
154
|
-
# assert_equal Person, PersonObserver.observed_class
|
155
|
-
def observed_class
|
156
|
-
if observed_class_name = name[/(.*)Observer/, 1]
|
157
|
-
observed_class_name.constantize
|
158
|
-
else
|
159
|
-
nil
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
89
|
+
class Observer < ActiveModel::Observer
|
90
|
+
class_attribute :observed_methods
|
91
|
+
self.observed_methods = []
|
163
92
|
|
164
|
-
# Start observing the declared classes and their subclasses.
|
165
93
|
def initialize
|
166
|
-
|
94
|
+
super
|
95
|
+
observed_subclasses.each { |klass| add_observer!(klass) }
|
167
96
|
end
|
168
97
|
|
169
|
-
|
170
|
-
|
171
|
-
send(observed_method, object) if respond_to?(observed_method)
|
172
|
-
end
|
173
|
-
|
174
|
-
# Special method sent by the observed class when it is inherited.
|
175
|
-
# Passes the new subclass.
|
176
|
-
def observed_class_inherited(subclass) #:nodoc:
|
177
|
-
self.class.observe(observed_classes + [subclass])
|
178
|
-
add_observer!(subclass)
|
98
|
+
def self.method_added(method)
|
99
|
+
self.observed_methods += [method] if ActiveRecord::Callbacks::CALLBACKS.include?(method.to_sym)
|
179
100
|
end
|
180
101
|
|
181
102
|
protected
|
182
|
-
def observed_classes
|
183
|
-
Set.new([self.class.observed_class].compact.flatten)
|
184
|
-
end
|
185
|
-
|
186
103
|
def observed_subclasses
|
187
104
|
observed_classes.sum([]) { |klass| klass.send(:subclasses) }
|
188
105
|
end
|
189
106
|
|
190
107
|
def add_observer!(klass)
|
191
|
-
|
192
|
-
|
193
|
-
|
108
|
+
super
|
109
|
+
|
110
|
+
# Check if a notifier callback was already added to the given class. If
|
111
|
+
# it was not, add it.
|
112
|
+
self.class.observed_methods.each do |method|
|
113
|
+
callback = :"_notify_observers_for_#{method}"
|
114
|
+
if (klass.instance_methods & [callback, callback.to_s]).empty?
|
115
|
+
klass.class_eval "def #{callback}; notify_observers(:#{method}); end"
|
116
|
+
klass.send(method, callback)
|
117
|
+
end
|
194
118
|
end
|
195
119
|
end
|
196
120
|
end
|