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
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
# ones created with +build+ are added to the target. So, the target may be
|
12
12
|
# non-empty and still lack children waiting to be read from the database.
|
13
13
|
# If you look directly to the database you cannot assume that's the entire
|
14
|
-
# collection because new records may have
|
14
|
+
# collection because new records may have been added to the target, etc.
|
15
15
|
#
|
16
16
|
# If you need to work on all current children, new and existing records,
|
17
17
|
# +load_target+ and the +loaded+ flag are your friends.
|
@@ -20,7 +20,22 @@ module ActiveRecord
|
|
20
20
|
super
|
21
21
|
construct_sql
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
|
+
delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
|
25
|
+
|
26
|
+
def select(select = nil, &block)
|
27
|
+
if block_given?
|
28
|
+
load_target
|
29
|
+
@target.select(&block)
|
30
|
+
else
|
31
|
+
scoped.select(select)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def scoped
|
36
|
+
with_scope(construct_scope) { @reflection.klass.scoped }
|
37
|
+
end
|
38
|
+
|
24
39
|
def find(*args)
|
25
40
|
options = args.extract_options!
|
26
41
|
|
@@ -37,27 +52,24 @@ module ActiveRecord
|
|
37
52
|
load_target.select { |r| ids.include?(r.id) }
|
38
53
|
end
|
39
54
|
else
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
options[:conditions] = conditions
|
55
|
+
merge_options_from_reflection!(options)
|
56
|
+
construct_find_options!(options)
|
57
|
+
|
58
|
+
find_scope = construct_scope[:find].slice(:conditions, :order)
|
46
59
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
60
|
+
with_scope(:find => find_scope) do
|
61
|
+
relation = @reflection.klass.send(:construct_finder_arel, options, @reflection.klass.send(:current_scoped_methods))
|
62
|
+
|
63
|
+
case args.first
|
64
|
+
when :first, :last
|
65
|
+
relation.send(args.first)
|
66
|
+
when :all
|
67
|
+
records = relation.all
|
68
|
+
@reflection.options[:uniq] ? uniq(records) : records
|
69
|
+
else
|
70
|
+
relation.find(*args)
|
71
|
+
end
|
51
72
|
end
|
52
|
-
|
53
|
-
# Build options specific to association
|
54
|
-
construct_find_options!(options)
|
55
|
-
|
56
|
-
merge_options_from_reflection!(options)
|
57
|
-
|
58
|
-
# Pass through args exactly as we received them.
|
59
|
-
args << options
|
60
|
-
@reflection.klass.find(*args)
|
61
73
|
end
|
62
74
|
end
|
63
75
|
|
@@ -164,14 +176,15 @@ module ActiveRecord
|
|
164
176
|
# be used for the query. If no +:counter_sql+ was supplied, but +:finder_sql+ was set, the
|
165
177
|
# descendant's +construct_sql+ method will have set :counter_sql automatically.
|
166
178
|
# Otherwise, construct options and pass them with scope to the target class's +count+.
|
167
|
-
def count(
|
179
|
+
def count(column_name = nil, options = {})
|
168
180
|
if @reflection.options[:counter_sql]
|
169
181
|
@reflection.klass.count_by_sql(@counter_sql)
|
170
182
|
else
|
171
|
-
column_name, options =
|
183
|
+
column_name, options = nil, column_name if column_name.is_a?(Hash)
|
184
|
+
|
172
185
|
if @reflection.options[:uniq]
|
173
186
|
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
174
|
-
column_name = "#{@reflection.quoted_table_name}.#{@reflection.klass.primary_key}"
|
187
|
+
column_name = "#{@reflection.quoted_table_name}.#{@reflection.klass.primary_key}" unless column_name
|
175
188
|
options.merge!(:distinct => true)
|
176
189
|
end
|
177
190
|
|
@@ -229,14 +242,13 @@ module ActiveRecord
|
|
229
242
|
self
|
230
243
|
end
|
231
244
|
|
232
|
-
#
|
245
|
+
# Destroy all the records from this association.
|
233
246
|
#
|
234
247
|
# See destroy for more info.
|
235
248
|
def destroy_all
|
236
249
|
load_target
|
237
|
-
destroy(@target)
|
238
|
-
|
239
|
-
end
|
250
|
+
destroy(@target)
|
251
|
+
reset_target!
|
240
252
|
end
|
241
253
|
|
242
254
|
def create(attrs = {})
|
@@ -304,6 +316,15 @@ module ActiveRecord
|
|
304
316
|
end
|
305
317
|
end
|
306
318
|
|
319
|
+
# Returns true if the collection has more than 1 record. Equivalent to collection.size > 1.
|
320
|
+
def many?
|
321
|
+
if block_given?
|
322
|
+
method_missing(:many?) { |*block_args| yield(*block_args) }
|
323
|
+
else
|
324
|
+
size > 1
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
307
328
|
def uniq(collection = self)
|
308
329
|
seen = Set.new
|
309
330
|
collection.inject([]) do |kept, record|
|
@@ -332,7 +353,6 @@ module ActiveRecord
|
|
332
353
|
|
333
354
|
def include?(record)
|
334
355
|
return false unless record.is_a?(@reflection.klass)
|
335
|
-
return include_in_memory?(record) if record.new_record?
|
336
356
|
load_target if @reflection.options[:finder_sql] && !loaded?
|
337
357
|
return @target.include?(record) if loaded?
|
338
358
|
exists?(record)
|
@@ -345,22 +365,25 @@ module ActiveRecord
|
|
345
365
|
protected
|
346
366
|
def construct_find_options!(options)
|
347
367
|
end
|
348
|
-
|
368
|
+
|
369
|
+
def construct_counter_sql
|
370
|
+
if @reflection.options[:counter_sql]
|
371
|
+
@counter_sql = interpolate_sql(@reflection.options[:counter_sql])
|
372
|
+
elsif @reflection.options[:finder_sql]
|
373
|
+
# replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
|
374
|
+
@reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
|
375
|
+
@counter_sql = interpolate_sql(@reflection.options[:counter_sql])
|
376
|
+
else
|
377
|
+
@counter_sql = @finder_sql
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
349
381
|
def load_target
|
350
382
|
if !@owner.new_record? || foreign_key_present
|
351
383
|
begin
|
352
384
|
if !loaded?
|
353
385
|
if @target.is_a?(Array) && @target.any?
|
354
|
-
@target = find_target.
|
355
|
-
i = @target.index(f)
|
356
|
-
t = @target.delete_at(i) if i
|
357
|
-
if t && t.changed?
|
358
|
-
t
|
359
|
-
else
|
360
|
-
f.mark_for_destruction if t && t.marked_for_destruction?
|
361
|
-
f
|
362
|
-
end
|
363
|
-
end + @target.find_all {|t| t.new_record?}
|
386
|
+
@target = find_target + @target.find_all {|t| t.new_record? }
|
364
387
|
else
|
365
388
|
@target = find_target
|
366
389
|
end
|
@@ -373,29 +396,15 @@ module ActiveRecord
|
|
373
396
|
loaded if target
|
374
397
|
target
|
375
398
|
end
|
376
|
-
|
377
|
-
def method_missing(method, *args, &block)
|
378
|
-
case method.to_s
|
379
|
-
when 'find_or_create'
|
380
|
-
return find(:first, :conditions => args.first) || create(args.first)
|
381
|
-
when /^find_or_create_by_(.*)$/
|
382
|
-
rest = $1
|
383
|
-
find_args = pull_finder_args_from(DynamicFinderMatch.match(method).attribute_names, *args)
|
384
|
-
return send("find_by_#{rest}", *find_args) ||
|
385
|
-
method_missing("create_by_#{rest}", *args, &block)
|
386
|
-
when /^create_by_(.*)$/
|
387
|
-
return create($1.split('_and_').zip(args).inject({}) { |h,kv| k,v=kv ; h[k] = v ; h }, &block)
|
388
|
-
end
|
389
399
|
|
400
|
+
def method_missing(method, *args)
|
390
401
|
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
|
391
402
|
if block_given?
|
392
403
|
super { |*block_args| yield(*block_args) }
|
393
404
|
else
|
394
405
|
super
|
395
406
|
end
|
396
|
-
|
397
|
-
@reflection.klass.scopes[method].call(self, *args)
|
398
|
-
else
|
407
|
+
else
|
399
408
|
with_scope(construct_scope) do
|
400
409
|
if block_given?
|
401
410
|
@reflection.klass.send(method, *args) { |*block_args| yield(*block_args) }
|
@@ -430,36 +439,7 @@ module ActiveRecord
|
|
430
439
|
records
|
431
440
|
end
|
432
441
|
|
433
|
-
def add_record_to_target_with_callbacks(record)
|
434
|
-
callback(:before_add, record)
|
435
|
-
yield(record) if block_given?
|
436
|
-
@target ||= [] unless loaded?
|
437
|
-
@target << record unless @reflection.options[:uniq] && @target.include?(record)
|
438
|
-
callback(:after_add, record)
|
439
|
-
set_inverse_instance(record, @owner)
|
440
|
-
record
|
441
|
-
end
|
442
|
-
|
443
442
|
private
|
444
|
-
# Separate the "finder" args from the "create" args given to a
|
445
|
-
# find_or_create_by_ call. Returns an array with the
|
446
|
-
# parameter values in the same order as the keys in the
|
447
|
-
# "names" array. This code was based on code in base.rb's
|
448
|
-
# method_missing method.
|
449
|
-
def pull_finder_args_from(names, *args)
|
450
|
-
attributes = names.collect { |name| name.intern }
|
451
|
-
attribute_hash = {}
|
452
|
-
args.each_with_index do |arg, i|
|
453
|
-
if arg.is_a?(Hash)
|
454
|
-
attribute_hash.merge! arg
|
455
|
-
else
|
456
|
-
attribute_hash[attributes[i]] = arg
|
457
|
-
end
|
458
|
-
end
|
459
|
-
attribute_hash = attribute_hash.with_indifferent_access
|
460
|
-
attributes.collect { |attr| attribute_hash[attr] }
|
461
|
-
end
|
462
|
-
|
463
443
|
def create_record(attrs)
|
464
444
|
attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
|
465
445
|
ensure_owner_is_not_new
|
@@ -483,6 +463,16 @@ module ActiveRecord
|
|
483
463
|
end
|
484
464
|
end
|
485
465
|
|
466
|
+
def add_record_to_target_with_callbacks(record)
|
467
|
+
callback(:before_add, record)
|
468
|
+
yield(record) if block_given?
|
469
|
+
@target ||= [] unless loaded?
|
470
|
+
@target << record unless @reflection.options[:uniq] && @target.include?(record)
|
471
|
+
callback(:after_add, record)
|
472
|
+
set_inverse_instance(record, @owner)
|
473
|
+
record
|
474
|
+
end
|
475
|
+
|
486
476
|
def remove_records(*records)
|
487
477
|
records = flatten_deeper(records)
|
488
478
|
records.each { |record| raise_on_type_mismatch(record) }
|
@@ -497,15 +487,22 @@ module ActiveRecord
|
|
497
487
|
|
498
488
|
def callback(method, record)
|
499
489
|
callbacks_for(method).each do |callback|
|
500
|
-
|
490
|
+
case callback
|
491
|
+
when Symbol
|
492
|
+
@owner.send(callback, record)
|
493
|
+
when Proc
|
494
|
+
callback.call(@owner, record)
|
495
|
+
else
|
496
|
+
callback.send(method, @owner, record)
|
497
|
+
end
|
501
498
|
end
|
502
499
|
end
|
503
500
|
|
504
501
|
def callbacks_for(callback_name)
|
505
502
|
full_callback_name = "#{callback_name}_for_#{@reflection.name}"
|
506
503
|
@owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
|
507
|
-
end
|
508
|
-
|
504
|
+
end
|
505
|
+
|
509
506
|
def ensure_owner_is_not_new
|
510
507
|
if @owner.new_record?
|
511
508
|
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
@@ -516,18 +513,6 @@ module ActiveRecord
|
|
516
513
|
args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] ||
|
517
514
|
@target.any? { |record| record.new_record? } || args.first.kind_of?(Integer))
|
518
515
|
end
|
519
|
-
|
520
|
-
def include_in_memory?(record)
|
521
|
-
if @reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
522
|
-
@owner.send(proxy_reflection.through_reflection.name.to_sym).each do |source|
|
523
|
-
source_reflection_target = source.send(proxy_reflection.source_reflection.name)
|
524
|
-
return true if source_reflection_target.respond_to?(:include?) ? source_reflection_target.include?(record) : source_reflection_target == record
|
525
|
-
end
|
526
|
-
false
|
527
|
-
else
|
528
|
-
@target.include?(record)
|
529
|
-
end
|
530
|
-
end
|
531
516
|
end
|
532
517
|
end
|
533
518
|
end
|
@@ -156,21 +156,12 @@ module ActiveRecord
|
|
156
156
|
@reflection.options[:dependent]
|
157
157
|
end
|
158
158
|
|
159
|
-
# Returns a string with the IDs of +records+ joined with a comma, quoted
|
160
|
-
# if needed. The result is ready to be inserted into a SQL IN clause.
|
161
|
-
#
|
162
|
-
# quoted_record_ids(records) # => "23,56,58,67"
|
163
|
-
#
|
164
|
-
def quoted_record_ids(records)
|
165
|
-
records.map { |record| record.quoted_id }.join(',')
|
166
|
-
end
|
167
|
-
|
168
159
|
def interpolate_sql(sql, record = nil)
|
169
160
|
@owner.send(:interpolate_sql, sql, record)
|
170
161
|
end
|
171
162
|
|
172
163
|
# Forwards the call to the reflection class.
|
173
|
-
def sanitize_sql(sql, table_name = @reflection.klass.
|
164
|
+
def sanitize_sql(sql, table_name = @reflection.klass.table_name)
|
174
165
|
@reflection.klass.send(:sanitize_sql, sql, table_name)
|
175
166
|
end
|
176
167
|
|
@@ -209,12 +200,17 @@ module ActiveRecord
|
|
209
200
|
|
210
201
|
private
|
211
202
|
# Forwards any missing method call to the \target.
|
212
|
-
def method_missing(method, *args
|
203
|
+
def method_missing(method, *args)
|
213
204
|
if load_target
|
214
|
-
|
215
|
-
@target.
|
205
|
+
unless @target.respond_to?(method)
|
206
|
+
message = "undefined method `#{method.to_s}' for \"#{@target}\":#{@target.class.to_s}"
|
207
|
+
raise NoMethodError, message
|
208
|
+
end
|
209
|
+
|
210
|
+
if block_given?
|
211
|
+
@target.send(method, *args) { |*block_args| yield(*block_args) }
|
216
212
|
else
|
217
|
-
|
213
|
+
@target.send(method, *args)
|
218
214
|
end
|
219
215
|
end
|
220
216
|
end
|
@@ -260,10 +256,16 @@ module ActiveRecord
|
|
260
256
|
end
|
261
257
|
end
|
262
258
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
259
|
+
if RUBY_VERSION < '1.9.2'
|
260
|
+
# Array#flatten has problems with recursive arrays before Ruby 1.9.2.
|
261
|
+
# Going one level deeper solves the majority of the problems.
|
262
|
+
def flatten_deeper(array)
|
263
|
+
array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten
|
264
|
+
end
|
265
|
+
else
|
266
|
+
def flatten_deeper(array)
|
267
|
+
array.flatten
|
268
|
+
end
|
267
269
|
end
|
268
270
|
|
269
271
|
# Returns the ID of the owner, quoted if needed.
|
@@ -36,11 +36,11 @@ module ActiveRecord
|
|
36
36
|
loaded
|
37
37
|
record
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
def updated?
|
41
41
|
@updated
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
private
|
45
45
|
def find_target
|
46
46
|
find_method = if @reflection.options[:primary_key]
|
@@ -63,6 +63,12 @@ module ActiveRecord
|
|
63
63
|
!@owner[@reflection.primary_key_name].nil?
|
64
64
|
end
|
65
65
|
|
66
|
+
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
67
|
+
# has_one associations.
|
68
|
+
def we_can_set_the_inverse_on_this?(record)
|
69
|
+
@reflection.has_inverse? && @reflection.inverse_of.macro == :has_one
|
70
|
+
end
|
71
|
+
|
66
72
|
def record_id(record)
|
67
73
|
record.send(@reflection.options[:primary_key] || :id)
|
68
74
|
end
|
@@ -75,12 +81,6 @@ module ActiveRecord
|
|
75
81
|
@owner[@reflection.primary_key_name]
|
76
82
|
end
|
77
83
|
end
|
78
|
-
|
79
|
-
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
80
|
-
# has_one associations.
|
81
|
-
def we_can_set_the_inverse_on_this?(record)
|
82
|
-
@reflection.has_inverse? && @reflection.inverse_of.macro == :has_one
|
83
|
-
end
|
84
84
|
end
|
85
85
|
end
|
86
86
|
end
|
@@ -1,11 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
3
|
class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
|
4
|
-
def initialize(owner, reflection)
|
5
|
-
super
|
6
|
-
@primary_key_list = {}
|
7
|
-
end
|
8
|
-
|
9
4
|
def create(attributes = {})
|
10
5
|
create_record(attributes) { |record| insert_record(record) }
|
11
6
|
end
|
@@ -23,9 +18,7 @@ module ActiveRecord
|
|
23
18
|
end
|
24
19
|
|
25
20
|
def has_primary_key?
|
26
|
-
|
27
|
-
@has_primary_key = (@owner.connection.supports_primary_key? &&
|
28
|
-
@owner.connection.primary_key(@reflection.options[:join_table]))
|
21
|
+
@has_primary_key ||= @owner.connection.supports_primary_key? && @owner.connection.primary_key(@reflection.options[:join_table])
|
29
22
|
end
|
30
23
|
|
31
24
|
protected
|
@@ -34,48 +27,40 @@ module ActiveRecord
|
|
34
27
|
options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select])
|
35
28
|
options[:select] ||= (@reflection.options[:select] || '*')
|
36
29
|
end
|
37
|
-
|
30
|
+
|
38
31
|
def count_records
|
39
32
|
load_target.size
|
40
33
|
end
|
41
34
|
|
42
35
|
def insert_record(record, force = true, validate = true)
|
43
|
-
if has_primary_key?
|
44
|
-
raise ActiveRecord::ConfigurationError,
|
45
|
-
"Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})."
|
46
|
-
end
|
47
|
-
|
48
36
|
if record.new_record?
|
49
37
|
if force
|
50
38
|
record.save!
|
51
39
|
else
|
52
|
-
return false unless record.save(validate)
|
40
|
+
return false unless record.save(:validate => validate)
|
53
41
|
end
|
54
42
|
end
|
55
43
|
|
56
44
|
if @reflection.options[:insert_sql]
|
57
45
|
@owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record))
|
58
46
|
else
|
47
|
+
relation = Arel::Table.new(@reflection.options[:join_table])
|
59
48
|
attributes = columns.inject({}) do |attrs, column|
|
60
49
|
case column.name.to_s
|
61
50
|
when @reflection.primary_key_name.to_s
|
62
|
-
attrs[column.name] = owner_quoted_id
|
51
|
+
attrs[relation[column.name]] = owner_quoted_id
|
63
52
|
when @reflection.association_foreign_key.to_s
|
64
|
-
attrs[column.name] = record.quoted_id
|
53
|
+
attrs[relation[column.name]] = record.quoted_id
|
65
54
|
else
|
66
55
|
if record.has_attribute?(column.name)
|
67
56
|
value = @owner.send(:quote_value, record[column.name], column)
|
68
|
-
attrs[column.name] = value unless value.nil?
|
57
|
+
attrs[relation[column.name]] = value unless value.nil?
|
69
58
|
end
|
70
59
|
end
|
71
60
|
attrs
|
72
61
|
end
|
73
62
|
|
74
|
-
|
75
|
-
"INSERT INTO #{@owner.connection.quote_table_name @reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
|
76
|
-
"VALUES (#{attributes.values.join(', ')})"
|
77
|
-
|
78
|
-
@owner.connection.insert(sql)
|
63
|
+
relation.insert(attributes)
|
79
64
|
end
|
80
65
|
|
81
66
|
return true
|
@@ -85,9 +70,10 @@ module ActiveRecord
|
|
85
70
|
if sql = @reflection.options[:delete_sql]
|
86
71
|
records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) }
|
87
72
|
else
|
88
|
-
|
89
|
-
|
90
|
-
|
73
|
+
relation = Arel::Table.new(@reflection.options[:join_table])
|
74
|
+
relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
|
75
|
+
and(Arel::Predicates::In.new(relation[@reflection.association_foreign_key], records.map(&:id)))
|
76
|
+
).delete
|
91
77
|
end
|
92
78
|
end
|
93
79
|
|
@@ -101,15 +87,7 @@ module ActiveRecord
|
|
101
87
|
|
102
88
|
@join_sql = "INNER JOIN #{@owner.connection.quote_table_name @reflection.options[:join_table]} ON #{@reflection.quoted_table_name}.#{@reflection.klass.primary_key} = #{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
|
103
89
|
|
104
|
-
|
105
|
-
@counter_sql = interpolate_sql(@reflection.options[:counter_sql])
|
106
|
-
elsif @reflection.options[:finder_sql]
|
107
|
-
# replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
|
108
|
-
@reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
|
109
|
-
@counter_sql = interpolate_sql(@reflection.options[:counter_sql])
|
110
|
-
else
|
111
|
-
@counter_sql = @finder_sql
|
112
|
-
end
|
90
|
+
construct_counter_sql
|
113
91
|
end
|
114
92
|
|
115
93
|
def construct_scope
|