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,3 +1,6 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
require 'active_support/core_ext/enumerable'
|
3
|
+
|
1
4
|
module ActiveRecord
|
2
5
|
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
|
3
6
|
def initialize(reflection, associated_class = nil)
|
@@ -45,7 +48,6 @@ module ActiveRecord
|
|
45
48
|
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
|
46
49
|
end
|
47
50
|
end
|
48
|
-
HasManyThroughCantAssociateThroughHasManyReflection = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection', 'ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection')
|
49
51
|
|
50
52
|
class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
|
51
53
|
def initialize(owner, reflection)
|
@@ -59,6 +61,12 @@ module ActiveRecord
|
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
64
|
+
class HasAndBelongsToManyAssociationWithPrimaryKeyError < ActiveRecordError #:nodoc:
|
65
|
+
def initialize(reflection)
|
66
|
+
super("Primary key is not allowed in a has_and_belongs_to_many join table (#{reflection.options[:join_table]}).")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
62
70
|
class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
|
63
71
|
def initialize(reflection)
|
64
72
|
super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
|
@@ -79,6 +87,8 @@ module ActiveRecord
|
|
79
87
|
|
80
88
|
# See ActiveRecord::Associations::ClassMethods for documentation.
|
81
89
|
module Associations # :nodoc:
|
90
|
+
extend ActiveSupport::Concern
|
91
|
+
|
82
92
|
# These classes will be loaded when associations are created.
|
83
93
|
# So there is no need to eager load them.
|
84
94
|
autoload :AssociationCollection, 'active_record/associations/association_collection'
|
@@ -91,10 +101,6 @@ module ActiveRecord
|
|
91
101
|
autoload :HasOneAssociation, 'active_record/associations/has_one_association'
|
92
102
|
autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association'
|
93
103
|
|
94
|
-
def self.included(base)
|
95
|
-
base.extend(ClassMethods)
|
96
|
-
end
|
97
|
-
|
98
104
|
# Clears out the association cache
|
99
105
|
def clear_association_cache #:nodoc:
|
100
106
|
self.class.reflect_on_all_associations.to_a.each do |assoc|
|
@@ -439,7 +445,7 @@ module ActiveRecord
|
|
439
445
|
# @group.users.collect { |u| u.avatar }.flatten # select all avatars for all users in the group
|
440
446
|
# @group.avatars # selects all avatars by going through the User join model.
|
441
447
|
#
|
442
|
-
# An important caveat with going through +has_one+ or +has_many+ associations on the join model is that these associations are
|
448
|
+
# An important caveat with going through +has_one+ or +has_many+ associations on the join model is that these associations are
|
443
449
|
# *read-only*. For example, the following would not work following the previous example:
|
444
450
|
#
|
445
451
|
# @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around.
|
@@ -545,16 +551,16 @@ module ActiveRecord
|
|
545
551
|
#
|
546
552
|
# Since only one table is loaded at a time, conditions or orders cannot reference tables other than the main one. If this is the case
|
547
553
|
# Active Record falls back to the previously used LEFT OUTER JOIN based strategy. For example
|
548
|
-
#
|
554
|
+
#
|
549
555
|
# Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true])
|
550
556
|
#
|
551
|
-
# will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
|
557
|
+
# This will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
|
552
558
|
# <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions like this can have unintended consequences.
|
553
559
|
# In the above example posts with no approved comments are not returned at all, because the conditions apply to the SQL statement as a whole
|
554
560
|
# and not just to the association. You must disambiguate column references for this fallback to happen, for example
|
555
|
-
# <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
|
561
|
+
# <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not.
|
556
562
|
#
|
557
|
-
# If you do want
|
563
|
+
# If you do want eager load only some members of an association it is usually more natural to <tt>:include</tt> an association
|
558
564
|
# which has conditions defined on it:
|
559
565
|
#
|
560
566
|
# class Post < ActiveRecord::Base
|
@@ -563,7 +569,7 @@ module ActiveRecord
|
|
563
569
|
#
|
564
570
|
# Post.find(:all, :include => :approved_comments)
|
565
571
|
#
|
566
|
-
# will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved.
|
572
|
+
# This will load posts and eager load the +approved_comments+ association, which contains only those comments that have been approved.
|
567
573
|
#
|
568
574
|
# If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored, returning all the associated objects:
|
569
575
|
#
|
@@ -586,10 +592,10 @@ module ActiveRecord
|
|
586
592
|
#
|
587
593
|
# Address.find(:all, :include => :addressable)
|
588
594
|
#
|
589
|
-
# will execute one query to load the addresses and load the addressables with one query per addressable type.
|
595
|
+
# This will execute one query to load the addresses and load the addressables with one query per addressable type.
|
590
596
|
# For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of
|
591
597
|
# addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback
|
592
|
-
# to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent
|
598
|
+
# to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent
|
593
599
|
# model's type is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that query.
|
594
600
|
#
|
595
601
|
# == Table Aliasing
|
@@ -670,6 +676,60 @@ module ActiveRecord
|
|
670
676
|
# end
|
671
677
|
# end
|
672
678
|
#
|
679
|
+
# == Bi-directional associations
|
680
|
+
#
|
681
|
+
# When you specify an association there is usually an association on the associated model that specifies the same
|
682
|
+
# relationship in reverse. For example, with the following models:
|
683
|
+
#
|
684
|
+
# class Dungeon < ActiveRecord::Base
|
685
|
+
# has_many :traps
|
686
|
+
# has_one :evil_wizard
|
687
|
+
# end
|
688
|
+
#
|
689
|
+
# class Trap < ActiveRecord::Base
|
690
|
+
# belongs_to :dungeon
|
691
|
+
# end
|
692
|
+
#
|
693
|
+
# class EvilWizard < ActiveRecord::Base
|
694
|
+
# belongs_to :dungeon
|
695
|
+
# end
|
696
|
+
#
|
697
|
+
# The +traps+ association on +Dungeon+ and the the +dungeon+ association on +Trap+ are the inverse of each other and the
|
698
|
+
# inverse of the +dungeon+ association on +EvilWizard+ is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
|
699
|
+
# +ActiveRecord+ doesn't do know anything about these inverse relationships and so no object loading optimisation is possible. For example:
|
700
|
+
#
|
701
|
+
# d = Dungeon.first
|
702
|
+
# t = d.traps.first
|
703
|
+
# d.level == t.dungeon.level # => true
|
704
|
+
# d.level = 10
|
705
|
+
# d.level == t.dungeon.level # => false
|
706
|
+
#
|
707
|
+
# The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to the same object data from the database, but are
|
708
|
+
# actually different in-memory copies of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
|
709
|
+
# +ActiveRecord+ about inverse relationships and it will optimise object loading. For example, if we changed our model definitions to:
|
710
|
+
#
|
711
|
+
# class Dungeon < ActiveRecord::Base
|
712
|
+
# has_many :traps, :inverse_of => :dungeon
|
713
|
+
# has_one :evil_wizard, :inverse_of => :dungeon
|
714
|
+
# end
|
715
|
+
#
|
716
|
+
# class Trap < ActiveRecord::Base
|
717
|
+
# belongs_to :dungeon, :inverse_of => :traps
|
718
|
+
# end
|
719
|
+
#
|
720
|
+
# class EvilWizard < ActiveRecord::Base
|
721
|
+
# belongs_to :dungeon, :inverse_of => :evil_wizard
|
722
|
+
# end
|
723
|
+
#
|
724
|
+
# Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same in-memory instance and our final <tt>d.level == t.dungeon.level</tt>
|
725
|
+
# will return +true+.
|
726
|
+
#
|
727
|
+
# There are limitations to <tt>:inverse_of</tt> support:
|
728
|
+
#
|
729
|
+
# * does not work with <tt>:through</tt> associations.
|
730
|
+
# * does not work with <tt>:polymorphic</tt> associations.
|
731
|
+
# * for +belongs_to+ associations +has_many+ inverse associations are ignored.
|
732
|
+
#
|
673
733
|
# == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
|
674
734
|
#
|
675
735
|
# If you attempt to assign an object to an association that doesn't match the inferred or specified <tt>:class_name</tt>, you'll
|
@@ -714,13 +774,12 @@ module ActiveRecord
|
|
714
774
|
# [collection.build(attributes = {}, ...)]
|
715
775
|
# Returns one or more new objects of the collection type that have been instantiated
|
716
776
|
# with +attributes+ and linked to this object through a foreign key, but have not yet
|
717
|
-
# been saved.
|
718
|
-
# it's +nil+!
|
777
|
+
# been saved.
|
719
778
|
# [collection.create(attributes = {})]
|
720
779
|
# Returns a new object of the collection type that has been instantiated
|
721
780
|
# with +attributes+, linked to this object through a foreign key, and that has already
|
722
|
-
# been saved (if it passed the validation).
|
723
|
-
#
|
781
|
+
# been saved (if it passed the validation). *Note*: This only works if the base model
|
782
|
+
# already exists in the DB, not if it is a new (unsaved) record!
|
724
783
|
#
|
725
784
|
# (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
|
726
785
|
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
|
@@ -810,6 +869,10 @@ module ActiveRecord
|
|
810
869
|
# If false, don't validate the associated objects when saving the parent object. true by default.
|
811
870
|
# [:autosave]
|
812
871
|
# If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
|
872
|
+
# [:inverse_of]
|
873
|
+
# Specifies the name of the <tt>belongs_to</tt> association on the associated object that is the inverse of this <tt>has_many</tt>
|
874
|
+
# association. Does not work in combination with <tt>:through</tt> or <tt>:as</tt> options.
|
875
|
+
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail.
|
813
876
|
#
|
814
877
|
# Option examples:
|
815
878
|
# has_many :comments, :order => "posted_on"
|
@@ -906,7 +969,7 @@ module ActiveRecord
|
|
906
969
|
# but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error.
|
907
970
|
# [:through]
|
908
971
|
# Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
|
909
|
-
# are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a
|
972
|
+
# are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a
|
910
973
|
# <tt>has_one</tt> or <tt>belongs_to</tt> association on the join model.
|
911
974
|
# [:source]
|
912
975
|
# Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be
|
@@ -921,6 +984,10 @@ module ActiveRecord
|
|
921
984
|
# If false, don't validate the associated object when saving the parent object. +false+ by default.
|
922
985
|
# [:autosave]
|
923
986
|
# If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
|
987
|
+
# [:inverse_of]
|
988
|
+
# Specifies the name of the <tt>belongs_to</tt> association on the associated object that is the inverse of this <tt>has_one</tt>
|
989
|
+
# association. Does not work in combination with <tt>:through</tt> or <tt>:as</tt> options.
|
990
|
+
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail.
|
924
991
|
#
|
925
992
|
# Option examples:
|
926
993
|
# has_one :credit_card, :dependent => :destroy # destroys the associated credit card
|
@@ -972,7 +1039,6 @@ module ActiveRecord
|
|
972
1039
|
# A Post class declares <tt>belongs_to :author</tt>, which will add:
|
973
1040
|
# * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
|
974
1041
|
# * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
|
975
|
-
# * <tt>Post#author?</tt> (similar to <tt>post.author == some_author</tt>)
|
976
1042
|
# * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
|
977
1043
|
# * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
|
978
1044
|
# The declaration can also include an options hash to specialize the behavior of the association.
|
@@ -1023,6 +1089,10 @@ module ActiveRecord
|
|
1023
1089
|
# [:touch]
|
1024
1090
|
# If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or
|
1025
1091
|
# destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute.
|
1092
|
+
# [:inverse_of]
|
1093
|
+
# Specifies the name of the <tt>has_one</tt> or <tt>has_many</tt> association on the associated object that is the inverse of this <tt>belongs_to</tt>
|
1094
|
+
# association. Does not work in combination with the <tt>:polymorphic</tt> options.
|
1095
|
+
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail.
|
1026
1096
|
#
|
1027
1097
|
# Option examples:
|
1028
1098
|
# belongs_to :firm, :foreign_key => "client_of"
|
@@ -1163,8 +1233,8 @@ module ActiveRecord
|
|
1163
1233
|
# the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
|
1164
1234
|
# [:conditions]
|
1165
1235
|
# Specify the conditions that the associated object must meet in order to be included as a +WHERE+
|
1166
|
-
# SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used.
|
1167
|
-
# <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
|
1236
|
+
# SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used.
|
1237
|
+
# <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt>
|
1168
1238
|
# or <tt>@blog.posts.build</tt>.
|
1169
1239
|
# [:order]
|
1170
1240
|
# Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment,
|
@@ -1232,7 +1302,7 @@ module ActiveRecord
|
|
1232
1302
|
|
1233
1303
|
private
|
1234
1304
|
# Generates a join table name from two provided table names.
|
1235
|
-
# The names in the join table
|
1305
|
+
# The names in the join table names end up in lexicographic order.
|
1236
1306
|
#
|
1237
1307
|
# join_table_name("members", "clubs") # => "clubs_members"
|
1238
1308
|
# join_table_name("members", "special_clubs") # => "members_special_clubs"
|
@@ -1276,17 +1346,8 @@ module ActiveRecord
|
|
1276
1346
|
association = association_proxy_class.new(self, reflection)
|
1277
1347
|
end
|
1278
1348
|
|
1279
|
-
|
1280
|
-
|
1281
|
-
if new_record?
|
1282
|
-
association_instance_set(reflection.name, new_value.nil? ? nil : association)
|
1283
|
-
else
|
1284
|
-
self.send(reflection.name, new_value)
|
1285
|
-
end
|
1286
|
-
else
|
1287
|
-
association.replace(new_value)
|
1288
|
-
association_instance_set(reflection.name, new_value.nil? ? nil : association)
|
1289
|
-
end
|
1349
|
+
association.replace(new_value)
|
1350
|
+
association_instance_set(reflection.name, new_value.nil? ? nil : association)
|
1290
1351
|
end
|
1291
1352
|
|
1292
1353
|
define_method("set_#{reflection.name}_target") do |target|
|
@@ -1316,9 +1377,16 @@ module ActiveRecord
|
|
1316
1377
|
if send(reflection.name).loaded? || reflection.options[:finder_sql]
|
1317
1378
|
send(reflection.name).map(&:id)
|
1318
1379
|
else
|
1319
|
-
|
1380
|
+
if reflection.through_reflection && reflection.source_reflection.belongs_to?
|
1381
|
+
through = reflection.through_reflection
|
1382
|
+
primary_key = reflection.source_reflection.primary_key_name
|
1383
|
+
send(through.name).select("DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}")
|
1384
|
+
else
|
1385
|
+
send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map!(&:id)
|
1386
|
+
end
|
1320
1387
|
end
|
1321
1388
|
end
|
1389
|
+
|
1322
1390
|
end
|
1323
1391
|
|
1324
1392
|
def collection_accessor_methods(reflection, association_proxy_class, writer = true)
|
@@ -1379,12 +1447,12 @@ module ActiveRecord
|
|
1379
1447
|
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
|
1380
1448
|
)
|
1381
1449
|
end
|
1382
|
-
|
1450
|
+
|
1383
1451
|
def add_touch_callbacks(reflection, touch_attribute)
|
1384
1452
|
method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym
|
1385
1453
|
define_method(method_name) do
|
1386
1454
|
association = send(reflection.name)
|
1387
|
-
|
1455
|
+
|
1388
1456
|
if touch_attribute == true
|
1389
1457
|
association.touch unless association.nil?
|
1390
1458
|
else
|
@@ -1395,15 +1463,6 @@ module ActiveRecord
|
|
1395
1463
|
after_destroy(method_name)
|
1396
1464
|
end
|
1397
1465
|
|
1398
|
-
def find_with_associations(options = {})
|
1399
|
-
catch :invalid_query do
|
1400
|
-
join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
|
1401
|
-
rows = select_all_rows(options, join_dependency)
|
1402
|
-
return join_dependency.instantiate(rows)
|
1403
|
-
end
|
1404
|
-
[]
|
1405
|
-
end
|
1406
|
-
|
1407
1466
|
# Creates before_destroy callback methods that nullify, delete or destroy
|
1408
1467
|
# has_many associated objects, according to the defined :dependent rule.
|
1409
1468
|
#
|
@@ -1415,39 +1474,59 @@ module ActiveRecord
|
|
1415
1474
|
# finder conditions.
|
1416
1475
|
def configure_dependency_for_has_many(reflection, extra_conditions = nil)
|
1417
1476
|
if reflection.options.include?(:dependent)
|
1477
|
+
# Add polymorphic type if the :as option is present
|
1478
|
+
dependent_conditions = []
|
1479
|
+
dependent_conditions << "#{reflection.primary_key_name} = \#{record.#{reflection.name}.send(:owner_quoted_id)}"
|
1480
|
+
dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
|
1481
|
+
dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.table_name) if reflection.options[:conditions]
|
1482
|
+
dependent_conditions << extra_conditions if extra_conditions
|
1483
|
+
dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
|
1484
|
+
dependent_conditions = dependent_conditions.gsub('@', '\@')
|
1418
1485
|
case reflection.options[:dependent]
|
1419
1486
|
when :destroy
|
1420
1487
|
method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym
|
1421
1488
|
define_method(method_name) do
|
1422
|
-
send(reflection.name).each
|
1423
|
-
# No point in executing the counter update since we're going to destroy the parent anyway
|
1424
|
-
counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym
|
1425
|
-
if(o.respond_to? counter_method) then
|
1426
|
-
class << o
|
1427
|
-
self
|
1428
|
-
end.send(:define_method, counter_method, Proc.new {})
|
1429
|
-
end
|
1430
|
-
o.destroy
|
1431
|
-
end
|
1489
|
+
send(reflection.name).each { |o| o.destroy }
|
1432
1490
|
end
|
1433
1491
|
before_destroy method_name
|
1434
1492
|
when :delete_all
|
1435
|
-
before_destroy do |record|
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
end
|
1493
|
+
# before_destroy do |record|
|
1494
|
+
# self.class.send(:delete_all_has_many_dependencies,
|
1495
|
+
# record,
|
1496
|
+
# "posts",
|
1497
|
+
# Post,
|
1498
|
+
# %@...@) # this is a string literal like %(...)
|
1499
|
+
# end
|
1500
|
+
# end
|
1501
|
+
module_eval <<-CALLBACK
|
1502
|
+
before_destroy do |record|
|
1503
|
+
self.class.send(:delete_all_has_many_dependencies,
|
1504
|
+
record,
|
1505
|
+
"#{reflection.name}",
|
1506
|
+
#{reflection.class_name},
|
1507
|
+
%@#{dependent_conditions}@)
|
1508
|
+
end
|
1509
|
+
CALLBACK
|
1442
1510
|
when :nullify
|
1443
|
-
before_destroy do |record|
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
end
|
1511
|
+
# before_destroy do |record|
|
1512
|
+
# self.class.send(:nullify_has_many_dependencies,
|
1513
|
+
# record,
|
1514
|
+
# "posts",
|
1515
|
+
# Post,
|
1516
|
+
# "user_id",
|
1517
|
+
# %@...@) # this is a string literal like %(...)
|
1518
|
+
# end
|
1519
|
+
# end
|
1520
|
+
module_eval <<-CALLBACK
|
1521
|
+
before_destroy do |record|
|
1522
|
+
self.class.send(:nullify_has_many_dependencies,
|
1523
|
+
record,
|
1524
|
+
"#{reflection.name}",
|
1525
|
+
#{reflection.class_name},
|
1526
|
+
"#{reflection.primary_key_name}",
|
1527
|
+
%@#{dependent_conditions}@)
|
1528
|
+
end
|
1529
|
+
CALLBACK
|
1451
1530
|
else
|
1452
1531
|
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})"
|
1453
1532
|
end
|
@@ -1591,198 +1670,24 @@ module ActiveRecord
|
|
1591
1670
|
|
1592
1671
|
def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
|
1593
1672
|
options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association)
|
1594
|
-
|
1595
1673
|
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
|
1596
1674
|
|
1597
1675
|
reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
|
1598
|
-
|
1676
|
+
|
1599
1677
|
if reflection.association_foreign_key == reflection.primary_key_name
|
1600
1678
|
raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection)
|
1601
1679
|
end
|
1602
1680
|
|
1603
1681
|
reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name))
|
1604
|
-
|
1605
|
-
|
1606
|
-
end
|
1607
|
-
|
1608
|
-
def reflect_on_included_associations(associations)
|
1609
|
-
[ associations ].flatten.collect { |association| reflect_on_association(association.to_s.intern) }
|
1610
|
-
end
|
1611
|
-
|
1612
|
-
def guard_against_unlimitable_reflections(reflections, options)
|
1613
|
-
if (options[:offset] || options[:limit]) && !using_limitable_reflections?(reflections)
|
1614
|
-
raise(
|
1615
|
-
ConfigurationError,
|
1616
|
-
"You can not use offset and limit together with has_many or has_and_belongs_to_many associations"
|
1617
|
-
)
|
1618
|
-
end
|
1619
|
-
end
|
1620
|
-
|
1621
|
-
def select_all_rows(options, join_dependency)
|
1622
|
-
connection.select_all(
|
1623
|
-
construct_finder_sql_with_included_associations(options, join_dependency),
|
1624
|
-
"#{name} Load Including Associations"
|
1625
|
-
)
|
1626
|
-
end
|
1627
|
-
|
1628
|
-
def construct_finder_sql_with_included_associations(options, join_dependency)
|
1629
|
-
scope = scope(:find)
|
1630
|
-
sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
|
1631
|
-
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
1632
|
-
|
1633
|
-
add_joins!(sql, options[:joins], scope)
|
1634
|
-
add_conditions!(sql, options[:conditions], scope)
|
1635
|
-
add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
|
1636
|
-
|
1637
|
-
add_group!(sql, options[:group], options[:having], scope)
|
1638
|
-
add_order!(sql, options[:order], scope)
|
1639
|
-
add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections)
|
1640
|
-
add_lock!(sql, options, scope)
|
1641
|
-
|
1642
|
-
return sanitize_sql(sql)
|
1643
|
-
end
|
1644
|
-
|
1645
|
-
def add_limited_ids_condition!(sql, options, join_dependency)
|
1646
|
-
unless (id_list = select_limited_ids_list(options, join_dependency)).empty?
|
1647
|
-
sql << "#{condition_word(sql)} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) "
|
1648
|
-
else
|
1649
|
-
throw :invalid_query
|
1650
|
-
end
|
1651
|
-
end
|
1652
|
-
|
1653
|
-
def select_limited_ids_list(options, join_dependency)
|
1654
|
-
pk = columns_hash[primary_key]
|
1655
|
-
|
1656
|
-
connection.select_all(
|
1657
|
-
construct_finder_sql_for_association_limiting(options, join_dependency),
|
1658
|
-
"#{name} Load IDs For Limited Eager Loading"
|
1659
|
-
).collect { |row| connection.quote(row[primary_key], pk) }.join(", ")
|
1660
|
-
end
|
1661
|
-
|
1662
|
-
def construct_finder_sql_for_association_limiting(options, join_dependency)
|
1663
|
-
scope = scope(:find)
|
1664
|
-
|
1665
|
-
# Only join tables referenced in order or conditions since this is particularly slow on the pre-query.
|
1666
|
-
tables_from_conditions = conditions_tables(options)
|
1667
|
-
tables_from_order = order_tables(options)
|
1668
|
-
all_tables = tables_from_conditions + tables_from_order
|
1669
|
-
distinct_join_associations = all_tables.uniq.map{|table|
|
1670
|
-
join_dependency.joins_for_table_name(table)
|
1671
|
-
}.flatten.compact.uniq
|
1672
|
-
|
1673
|
-
order = options[:order]
|
1674
|
-
if scoped_order = (scope && scope[:order])
|
1675
|
-
order = order ? "#{order}, #{scoped_order}" : scoped_order
|
1682
|
+
if connection.supports_primary_key? && (connection.primary_key(reflection.options[:join_table]) rescue false)
|
1683
|
+
raise HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection)
|
1676
1684
|
end
|
1677
1685
|
|
1678
|
-
|
1679
|
-
sql = "SELECT "
|
1680
|
-
if is_distinct
|
1681
|
-
sql << connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", order)
|
1682
|
-
else
|
1683
|
-
sql << primary_key
|
1684
|
-
end
|
1685
|
-
sql << " FROM #{connection.quote_table_name table_name} "
|
1686
|
-
|
1687
|
-
if is_distinct
|
1688
|
-
sql << distinct_join_associations.collect { |assoc| assoc.association_join }.join
|
1689
|
-
add_joins!(sql, options[:joins], scope)
|
1690
|
-
end
|
1691
|
-
|
1692
|
-
add_conditions!(sql, options[:conditions], scope)
|
1693
|
-
add_group!(sql, options[:group], options[:having], scope)
|
1694
|
-
|
1695
|
-
if order && is_distinct
|
1696
|
-
connection.add_order_by_for_association_limiting!(sql, :order => order)
|
1697
|
-
else
|
1698
|
-
add_order!(sql, options[:order], scope)
|
1699
|
-
end
|
1700
|
-
|
1701
|
-
add_limit!(sql, options, scope)
|
1702
|
-
|
1703
|
-
return sanitize_sql(sql)
|
1704
|
-
end
|
1705
|
-
|
1706
|
-
def tables_in_string(string)
|
1707
|
-
return [] if string.blank?
|
1708
|
-
string.scan(/([\.a-zA-Z_]+).?\./).flatten
|
1709
|
-
end
|
1710
|
-
|
1711
|
-
def tables_in_hash(hash)
|
1712
|
-
return [] if hash.blank?
|
1713
|
-
tables = hash.map do |key, value|
|
1714
|
-
if value.is_a?(Hash)
|
1715
|
-
key.to_s
|
1716
|
-
else
|
1717
|
-
tables_in_string(key) if key.is_a?(String)
|
1718
|
-
end
|
1719
|
-
end
|
1720
|
-
tables.flatten.compact
|
1721
|
-
end
|
1722
|
-
|
1723
|
-
def conditions_tables(options)
|
1724
|
-
# look in both sets of conditions
|
1725
|
-
conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
|
1726
|
-
case cond
|
1727
|
-
when nil then all
|
1728
|
-
when Array then all << tables_in_string(cond.first)
|
1729
|
-
when Hash then all << tables_in_hash(cond)
|
1730
|
-
else all << tables_in_string(cond)
|
1731
|
-
end
|
1732
|
-
end
|
1733
|
-
conditions.flatten
|
1734
|
-
end
|
1735
|
-
|
1736
|
-
def order_tables(options)
|
1737
|
-
order = [options[:order], scope(:find, :order) ].join(", ")
|
1738
|
-
return [] unless order && order.is_a?(String)
|
1739
|
-
tables_in_string(order)
|
1740
|
-
end
|
1741
|
-
|
1742
|
-
def selects_tables(options)
|
1743
|
-
select = options[:select]
|
1744
|
-
return [] unless select && select.is_a?(String)
|
1745
|
-
tables_in_string(select)
|
1746
|
-
end
|
1747
|
-
|
1748
|
-
def joined_tables(options)
|
1749
|
-
scope = scope(:find)
|
1750
|
-
joins = options[:joins]
|
1751
|
-
merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins])
|
1752
|
-
[table_name] + case merged_joins
|
1753
|
-
when Symbol, Hash, Array
|
1754
|
-
if array_of_strings?(merged_joins)
|
1755
|
-
tables_in_string(merged_joins.join(' '))
|
1756
|
-
else
|
1757
|
-
join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil)
|
1758
|
-
join_dependency.join_associations.collect {|join_association| [join_association.aliased_join_table_name, join_association.aliased_table_name]}.flatten.compact
|
1759
|
-
end
|
1760
|
-
else
|
1761
|
-
tables_in_string(merged_joins)
|
1762
|
-
end
|
1763
|
-
end
|
1764
|
-
|
1765
|
-
# Checks if the conditions reference a table other than the current model table
|
1766
|
-
def include_eager_conditions?(options, tables = nil, joined_tables = nil)
|
1767
|
-
((tables || conditions_tables(options)) - (joined_tables || joined_tables(options))).any?
|
1768
|
-
end
|
1769
|
-
|
1770
|
-
# Checks if the query order references a table other than the current model's table.
|
1771
|
-
def include_eager_order?(options, tables = nil, joined_tables = nil)
|
1772
|
-
((tables || order_tables(options)) - (joined_tables || joined_tables(options))).any?
|
1773
|
-
end
|
1774
|
-
|
1775
|
-
def include_eager_select?(options, joined_tables = nil)
|
1776
|
-
(selects_tables(options) - (joined_tables || joined_tables(options))).any?
|
1777
|
-
end
|
1778
|
-
|
1779
|
-
def references_eager_loaded_tables?(options)
|
1780
|
-
joined_tables = joined_tables(options)
|
1781
|
-
include_eager_order?(options, nil, joined_tables) || include_eager_conditions?(options, nil, joined_tables) || include_eager_select?(options, joined_tables)
|
1686
|
+
reflection
|
1782
1687
|
end
|
1783
1688
|
|
1784
1689
|
def using_limitable_reflections?(reflections)
|
1785
|
-
reflections.
|
1690
|
+
reflections.collect(&:collection?).length.zero?
|
1786
1691
|
end
|
1787
1692
|
|
1788
1693
|
def column_aliases(join_dependency)
|
@@ -1804,10 +1709,6 @@ module ActiveRecord
|
|
1804
1709
|
end
|
1805
1710
|
end
|
1806
1711
|
|
1807
|
-
def condition_word(sql)
|
1808
|
-
sql =~ /where/i ? " AND " : "WHERE "
|
1809
|
-
end
|
1810
|
-
|
1811
1712
|
def create_extension_modules(association_id, block_extension, extensions)
|
1812
1713
|
if block_extension
|
1813
1714
|
extension_module_name = "#{self.to_s.demodulize}#{association_id.to_s.camelize}AssociationExtension"
|
@@ -1870,37 +1771,22 @@ module ActiveRecord
|
|
1870
1771
|
associations.keys.each do |name|
|
1871
1772
|
reflection = base.reflections[name]
|
1872
1773
|
|
1873
|
-
parent_records =
|
1874
|
-
|
1875
|
-
|
1876
|
-
|
1877
|
-
|
1878
|
-
|
1774
|
+
parent_records = []
|
1775
|
+
records.each do |record|
|
1776
|
+
if descendant = record.send(reflection.name)
|
1777
|
+
if reflection.collection?
|
1778
|
+
parent_records.concat descendant.target.uniq
|
1779
|
+
else
|
1780
|
+
parent_records << descendant
|
1781
|
+
end
|
1782
|
+
end
|
1783
|
+
end
|
1879
1784
|
|
1880
1785
|
remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty?
|
1881
1786
|
end
|
1882
1787
|
end
|
1883
1788
|
end
|
1884
1789
|
|
1885
|
-
def join_for_table_name(table_name)
|
1886
|
-
join = (@joins.select{|j|j.aliased_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first) rescue nil
|
1887
|
-
return join unless join.nil?
|
1888
|
-
@joins.select{|j|j.is_a?(JoinAssociation) && j.aliased_join_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first rescue nil
|
1889
|
-
end
|
1890
|
-
|
1891
|
-
def joins_for_table_name(table_name)
|
1892
|
-
join = join_for_table_name(table_name)
|
1893
|
-
result = nil
|
1894
|
-
if join && join.is_a?(JoinAssociation)
|
1895
|
-
result = [join]
|
1896
|
-
if join.parent && join.parent.is_a?(JoinAssociation)
|
1897
|
-
result = joins_for_table_name(join.parent.aliased_table_name) +
|
1898
|
-
result
|
1899
|
-
end
|
1900
|
-
end
|
1901
|
-
result
|
1902
|
-
end
|
1903
|
-
|
1904
1790
|
protected
|
1905
1791
|
def build(associations, parent = nil)
|
1906
1792
|
parent ||= @joins.last
|
@@ -1924,7 +1810,6 @@ module ActiveRecord
|
|
1924
1810
|
end
|
1925
1811
|
end
|
1926
1812
|
|
1927
|
-
# overridden in InnerJoinDependency subclass
|
1928
1813
|
def build_join_association(reflection, parent)
|
1929
1814
|
JoinAssociation.new(reflection, self, parent)
|
1930
1815
|
end
|
@@ -1987,7 +1872,7 @@ module ActiveRecord
|
|
1987
1872
|
|
1988
1873
|
class JoinBase # :nodoc:
|
1989
1874
|
attr_reader :active_record, :table_joins
|
1990
|
-
delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :to => :active_record
|
1875
|
+
delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record
|
1991
1876
|
|
1992
1877
|
def initialize(active_record, joins = nil)
|
1993
1878
|
@active_record = active_record
|
@@ -2049,6 +1934,7 @@ module ActiveRecord
|
|
2049
1934
|
@aliased_prefix = "t#{ join_dependency.joins.size }"
|
2050
1935
|
@parent_table_name = parent.active_record.table_name
|
2051
1936
|
@aliased_table_name = aliased_table_name_for(table_name)
|
1937
|
+
@join = nil
|
2052
1938
|
|
2053
1939
|
if reflection.macro == :has_and_belongs_to_many
|
2054
1940
|
@aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
|
@@ -2060,133 +1946,110 @@ module ActiveRecord
|
|
2060
1946
|
end
|
2061
1947
|
|
2062
1948
|
def association_join
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
2066
|
-
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2079
|
-
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2084
|
-
|
2085
|
-
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2090
|
-
connection.quote_table_name(aliased_join_table_name),
|
2091
|
-
connection.quote_column_name(through_reflection.options[:as].to_s + '_type'),
|
2092
|
-
klass.quote_value(parent.active_record.base_class.name)
|
2093
|
-
]
|
2094
|
-
else
|
2095
|
-
jt_foreign_key = through_reflection.primary_key_name
|
2096
|
-
end
|
1949
|
+
return @join if @join
|
1950
|
+
|
1951
|
+
aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine)
|
1952
|
+
parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => arel_engine)
|
1953
|
+
|
1954
|
+
@join = case reflection.macro
|
1955
|
+
when :has_and_belongs_to_many
|
1956
|
+
join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine)
|
1957
|
+
fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key
|
1958
|
+
klass_fk = options[:association_foreign_key] || klass.to_s.foreign_key
|
1959
|
+
|
1960
|
+
[
|
1961
|
+
join_table[fk].eq(parent_table[reflection.active_record.primary_key]),
|
1962
|
+
aliased_table[klass.primary_key].eq(join_table[klass_fk])
|
1963
|
+
]
|
1964
|
+
when :has_many, :has_one
|
1965
|
+
if reflection.options[:through]
|
1966
|
+
join_table = Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine)
|
1967
|
+
jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
|
1968
|
+
first_key = second_key = as_extra = nil
|
1969
|
+
|
1970
|
+
if through_reflection.options[:as] # has_many :through against a polymorphic join
|
1971
|
+
jt_foreign_key = through_reflection.options[:as].to_s + '_id'
|
1972
|
+
jt_as_extra = join_table[through_reflection.options[:as].to_s + '_type'].eq(parent.active_record.base_class.name)
|
1973
|
+
else
|
1974
|
+
jt_foreign_key = through_reflection.primary_key_name
|
1975
|
+
end
|
2097
1976
|
|
2098
|
-
|
2099
|
-
|
2100
|
-
|
2101
|
-
|
2102
|
-
|
2103
|
-
|
2104
|
-
|
2105
|
-
|
2106
|
-
|
2107
|
-
|
2108
|
-
else
|
2109
|
-
first_key = through_reflection.klass.base_class.to_s.foreign_key
|
2110
|
-
second_key = options[:foreign_key] || primary_key
|
2111
|
-
end
|
2112
|
-
|
2113
|
-
unless through_reflection.klass.descends_from_active_record?
|
2114
|
-
jt_sti_extra = " AND %s.%s = %s" % [
|
2115
|
-
connection.quote_table_name(aliased_join_table_name),
|
2116
|
-
connection.quote_column_name(through_reflection.active_record.inheritance_column),
|
2117
|
-
through_reflection.klass.quote_value(through_reflection.klass.sti_name)]
|
2118
|
-
end
|
2119
|
-
when :belongs_to
|
2120
|
-
first_key = primary_key
|
2121
|
-
if reflection.options[:source_type]
|
2122
|
-
second_key = source_reflection.association_foreign_key
|
2123
|
-
jt_source_extra = " AND %s.%s = %s" % [
|
2124
|
-
connection.quote_table_name(aliased_join_table_name),
|
2125
|
-
connection.quote_column_name(reflection.source_reflection.options[:foreign_type]),
|
2126
|
-
klass.quote_value(reflection.options[:source_type])
|
2127
|
-
]
|
2128
|
-
else
|
2129
|
-
second_key = source_reflection.primary_key_name
|
2130
|
-
end
|
2131
|
-
end
|
1977
|
+
case source_reflection.macro
|
1978
|
+
when :has_many
|
1979
|
+
if source_reflection.options[:as]
|
1980
|
+
first_key = "#{source_reflection.options[:as]}_id"
|
1981
|
+
second_key = options[:foreign_key] || primary_key
|
1982
|
+
as_extra = aliased_table["#{source_reflection.options[:as]}_type"].eq(source_reflection.active_record.base_class.name)
|
1983
|
+
else
|
1984
|
+
first_key = through_reflection.klass.base_class.to_s.foreign_key
|
1985
|
+
second_key = options[:foreign_key] || primary_key
|
1986
|
+
end
|
2132
1987
|
|
2133
|
-
|
2134
|
-
|
2135
|
-
|
2136
|
-
|
2137
|
-
|
2138
|
-
|
2139
|
-
|
2140
|
-
]
|
2141
|
-
" #{join_type} %s ON (%s.%s = %s.%s%s) " % [
|
2142
|
-
table_name_and_alias,
|
2143
|
-
connection.quote_table_name(aliased_table_name),
|
2144
|
-
connection.quote_column_name(first_key),
|
2145
|
-
connection.quote_table_name(aliased_join_table_name),
|
2146
|
-
connection.quote_column_name(second_key),
|
2147
|
-
as_extra
|
2148
|
-
]
|
2149
|
-
|
2150
|
-
when reflection.options[:as] && [:has_many, :has_one].include?(reflection.macro)
|
2151
|
-
" #{join_type} %s ON %s.%s = %s.%s AND %s.%s = %s" % [
|
2152
|
-
table_name_and_alias,
|
2153
|
-
connection.quote_table_name(aliased_table_name),
|
2154
|
-
"#{reflection.options[:as]}_id",
|
2155
|
-
connection.quote_table_name(parent.aliased_table_name),
|
2156
|
-
parent.primary_key,
|
2157
|
-
connection.quote_table_name(aliased_table_name),
|
2158
|
-
"#{reflection.options[:as]}_type",
|
2159
|
-
klass.quote_value(parent.active_record.base_class.name)
|
2160
|
-
]
|
1988
|
+
unless through_reflection.klass.descends_from_active_record?
|
1989
|
+
jt_sti_extra = join_table[through_reflection.active_record.inheritance_column].eq(through_reflection.klass.sti_name)
|
1990
|
+
end
|
1991
|
+
when :belongs_to
|
1992
|
+
first_key = primary_key
|
1993
|
+
if reflection.options[:source_type]
|
1994
|
+
second_key = source_reflection.association_foreign_key
|
1995
|
+
jt_source_extra = join_table[reflection.source_reflection.options[:foreign_type]].eq(reflection.options[:source_type])
|
2161
1996
|
else
|
2162
|
-
|
2163
|
-
|
2164
|
-
table_name_and_alias,
|
2165
|
-
aliased_table_name,
|
2166
|
-
foreign_key,
|
2167
|
-
parent.aliased_table_name,
|
2168
|
-
reflection.options[:primary_key] || parent.primary_key
|
2169
|
-
]
|
1997
|
+
second_key = source_reflection.primary_key_name
|
1998
|
+
end
|
2170
1999
|
end
|
2171
|
-
|
2172
|
-
|
2173
|
-
|
2174
|
-
|
2175
|
-
|
2176
|
-
|
2177
|
-
|
2178
|
-
|
2000
|
+
|
2001
|
+
[
|
2002
|
+
[parent_table[parent.primary_key].eq(join_table[jt_foreign_key]), jt_as_extra, jt_source_extra, jt_sti_extra].reject{|x| x.blank? },
|
2003
|
+
aliased_table[first_key].eq(join_table[second_key])
|
2004
|
+
]
|
2005
|
+
elsif reflection.options[:as]
|
2006
|
+
id_rel = aliased_table["#{reflection.options[:as]}_id"].eq(parent_table[parent.primary_key])
|
2007
|
+
type_rel = aliased_table["#{reflection.options[:as]}_type"].eq(parent.active_record.base_class.name)
|
2008
|
+
[id_rel, type_rel]
|
2179
2009
|
else
|
2180
|
-
|
2181
|
-
|
2182
|
-
|
2183
|
-
|
2010
|
+
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
|
2011
|
+
[aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])]
|
2012
|
+
end
|
2013
|
+
when :belongs_to
|
2014
|
+
[aliased_table[reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])]
|
2015
|
+
end
|
2016
|
+
|
2017
|
+
unless klass.descends_from_active_record?
|
2018
|
+
sti_column = aliased_table[klass.inheritance_column]
|
2019
|
+
sti_condition = sti_column.eq(klass.sti_name)
|
2020
|
+
klass.send(:subclasses).each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) }
|
2021
|
+
|
2022
|
+
@join << sti_condition
|
2023
|
+
end
|
2184
2024
|
|
2185
2025
|
[through_reflection, reflection].each do |ref|
|
2186
|
-
|
2026
|
+
if ref && ref.options[:conditions]
|
2027
|
+
@join << interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))
|
2028
|
+
end
|
2187
2029
|
end
|
2188
2030
|
|
2189
|
-
join
|
2031
|
+
@join
|
2032
|
+
end
|
2033
|
+
|
2034
|
+
def relation
|
2035
|
+
aliased = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine)
|
2036
|
+
|
2037
|
+
if reflection.macro == :has_and_belongs_to_many
|
2038
|
+
[Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine), aliased]
|
2039
|
+
elsif reflection.options[:through]
|
2040
|
+
[Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine), aliased]
|
2041
|
+
else
|
2042
|
+
aliased
|
2043
|
+
end
|
2044
|
+
end
|
2045
|
+
|
2046
|
+
def join_relation(joining_relation, join = nil)
|
2047
|
+
if (relations = relation).is_a?(Array)
|
2048
|
+
joining_relation.joins(Relation::JoinOperation.new(relations.first, Arel::OuterJoin, association_join.first)).
|
2049
|
+
joins(Relation::JoinOperation.new(relations.last, Arel::OuterJoin, association_join.last))
|
2050
|
+
else
|
2051
|
+
joining_relation.joins(Relation::JoinOperation.new(relations, Arel::OuterJoin, association_join))
|
2052
|
+
end
|
2190
2053
|
end
|
2191
2054
|
|
2192
2055
|
protected
|
@@ -2214,7 +2077,7 @@ module ActiveRecord
|
|
2214
2077
|
end
|
2215
2078
|
|
2216
2079
|
def table_alias_for(table_name, table_alias)
|
2217
|
-
"#{
|
2080
|
+
"#{table_name} #{table_alias if table_name != table_alias}".strip
|
2218
2081
|
end
|
2219
2082
|
|
2220
2083
|
def table_name_and_alias
|
@@ -2224,28 +2087,8 @@ module ActiveRecord
|
|
2224
2087
|
def interpolate_sql(sql)
|
2225
2088
|
instance_eval("%@#{sql.gsub('@', '\@')}@")
|
2226
2089
|
end
|
2227
|
-
|
2228
|
-
private
|
2229
|
-
def join_type
|
2230
|
-
"LEFT OUTER JOIN"
|
2231
|
-
end
|
2232
2090
|
end
|
2233
2091
|
end
|
2234
|
-
|
2235
|
-
class InnerJoinDependency < JoinDependency # :nodoc:
|
2236
|
-
protected
|
2237
|
-
def build_join_association(reflection, parent)
|
2238
|
-
InnerJoinAssociation.new(reflection, self, parent)
|
2239
|
-
end
|
2240
|
-
|
2241
|
-
class InnerJoinAssociation < JoinAssociation
|
2242
|
-
private
|
2243
|
-
def join_type
|
2244
|
-
"INNER JOIN"
|
2245
|
-
end
|
2246
|
-
end
|
2247
|
-
end
|
2248
|
-
|
2249
2092
|
end
|
2250
2093
|
end
|
2251
2094
|
end
|