activerecord 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1372 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +218 -0
- data/examples/performance.rb +184 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +173 -0
- data/lib/active_record/aggregations.rb +266 -0
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations.rb +1724 -0
- data/lib/active_record/associations/alias_tracker.rb +87 -0
- data/lib/active_record/associations/association.rb +253 -0
- data/lib/active_record/associations/association_scope.rb +194 -0
- data/lib/active_record/associations/belongs_to_association.rb +111 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
- data/lib/active_record/associations/builder/association.rb +149 -0
- data/lib/active_record/associations/builder/belongs_to.rb +116 -0
- data/lib/active_record/associations/builder/collection_association.rb +91 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
- data/lib/active_record/associations/builder/has_many.rb +15 -0
- data/lib/active_record/associations/builder/has_one.rb +23 -0
- data/lib/active_record/associations/builder/singular_association.rb +38 -0
- data/lib/active_record/associations/collection_association.rb +634 -0
- data/lib/active_record/associations/collection_proxy.rb +1027 -0
- data/lib/active_record/associations/has_many_association.rb +184 -0
- data/lib/active_record/associations/has_many_through_association.rb +238 -0
- data/lib/active_record/associations/has_one_association.rb +105 -0
- data/lib/active_record/associations/has_one_through_association.rb +36 -0
- data/lib/active_record/associations/join_dependency.rb +282 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/preloader.rb +203 -0
- data/lib/active_record/associations/preloader/association.rb +162 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +96 -0
- data/lib/active_record/associations/singular_association.rb +86 -0
- data/lib/active_record/associations/through_association.rb +96 -0
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +212 -0
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +439 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
- data/lib/active_record/attribute_methods/dirty.rb +181 -0
- data/lib/active_record/attribute_methods/primary_key.rb +128 -0
- data/lib/active_record/attribute_methods/query.rb +40 -0
- data/lib/active_record/attribute_methods/read.rb +103 -0
- data/lib/active_record/attribute_methods/serialization.rb +70 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
- data/lib/active_record/attribute_methods/write.rb +83 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +439 -0
- data/lib/active_record/base.rb +317 -0
- data/lib/active_record/callbacks.rb +313 -0
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +38 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
- data/lib/active_record/connection_adapters/column.rb +82 -0
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +566 -0
- data/lib/active_record/counter_cache.rb +175 -0
- data/lib/active_record/dynamic_matchers.rb +140 -0
- data/lib/active_record/enum.rb +198 -0
- data/lib/active_record/errors.rb +252 -0
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +1007 -0
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +247 -0
- data/lib/active_record/integration.rb +113 -0
- data/lib/active_record/locale/en.yml +47 -0
- data/lib/active_record/locking/optimistic.rb +204 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/log_subscriber.rb +75 -0
- data/lib/active_record/migration.rb +1051 -0
- data/lib/active_record/migration/command_recorder.rb +197 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +340 -0
- data/lib/active_record/nested_attributes.rb +548 -0
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +532 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/querying.rb +68 -0
- data/lib/active_record/railtie.rb +162 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +50 -0
- data/lib/active_record/railties/databases.rake +391 -0
- data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
- data/lib/active_record/readonly_attributes.rb +23 -0
- data/lib/active_record/reflection.rb +881 -0
- data/lib/active_record/relation.rb +681 -0
- data/lib/active_record/relation/batches.rb +138 -0
- data/lib/active_record/relation/calculations.rb +403 -0
- data/lib/active_record/relation/delegation.rb +140 -0
- data/lib/active_record/relation/finder_methods.rb +528 -0
- data/lib/active_record/relation/merger.rb +170 -0
- data/lib/active_record/relation/predicate_builder.rb +126 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +1176 -0
- data/lib/active_record/relation/spawn_methods.rb +75 -0
- data/lib/active_record/result.rb +131 -0
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +191 -0
- data/lib/active_record/schema.rb +64 -0
- data/lib/active_record/schema_dumper.rb +251 -0
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping.rb +87 -0
- data/lib/active_record/scoping/default.rb +134 -0
- data/lib/active_record/scoping/named.rb +164 -0
- data/lib/active_record/serialization.rb +22 -0
- data/lib/active_record/serializers/xml_serializer.rb +193 -0
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +205 -0
- data/lib/active_record/tasks/database_tasks.rb +296 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +121 -0
- data/lib/active_record/transactions.rb +417 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/validations.rb +90 -0
- data/lib/active_record/validations/associated.rb +51 -0
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +229 -0
- data/lib/active_record/version.rb +8 -0
- data/lib/rails/generators/active_record.rb +17 -0
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
- metadata +309 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
class CollectionAssociation < Association #:nodoc:
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def build_scope
|
9
|
+
super.order(preload_scope.values[:order] || reflection_scope.values[:order])
|
10
|
+
end
|
11
|
+
|
12
|
+
def preload(preloader)
|
13
|
+
associated_records_by_owner(preloader).each do |owner, records|
|
14
|
+
association = owner.association(reflection.name)
|
15
|
+
association.loaded!
|
16
|
+
association.target.concat(records)
|
17
|
+
records.each { |record| association.set_inverse_instance(record) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
class HasMany < CollectionAssociation #:nodoc:
|
5
|
+
|
6
|
+
def association_key_name
|
7
|
+
reflection.foreign_key
|
8
|
+
end
|
9
|
+
|
10
|
+
def owner_key_name
|
11
|
+
reflection.active_record_primary_key
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
class HasManyThrough < CollectionAssociation #:nodoc:
|
5
|
+
include ThroughAssociation
|
6
|
+
|
7
|
+
def associated_records_by_owner(preloader)
|
8
|
+
records_by_owner = super
|
9
|
+
|
10
|
+
if reflection_scope.distinct_value
|
11
|
+
records_by_owner.each_value { |records| records.uniq! }
|
12
|
+
end
|
13
|
+
|
14
|
+
records_by_owner
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
class HasOne < SingularAssociation #:nodoc:
|
5
|
+
|
6
|
+
def association_key_name
|
7
|
+
reflection.foreign_key
|
8
|
+
end
|
9
|
+
|
10
|
+
def owner_key_name
|
11
|
+
reflection.active_record_primary_key
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_scope
|
17
|
+
super.order(preload_scope.values[:order] || reflection_scope.values[:order])
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
class SingularAssociation < Association #:nodoc:
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def preload(preloader)
|
9
|
+
associated_records_by_owner(preloader).each do |owner, associated_records|
|
10
|
+
record = associated_records.first
|
11
|
+
|
12
|
+
association = owner.association(reflection.name)
|
13
|
+
association.target = record
|
14
|
+
association.set_inverse_instance(record) if record
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class Preloader
|
4
|
+
module ThroughAssociation #:nodoc:
|
5
|
+
def through_reflection
|
6
|
+
reflection.through_reflection
|
7
|
+
end
|
8
|
+
|
9
|
+
def source_reflection
|
10
|
+
reflection.source_reflection
|
11
|
+
end
|
12
|
+
|
13
|
+
def associated_records_by_owner(preloader)
|
14
|
+
preloader.preload(owners,
|
15
|
+
through_reflection.name,
|
16
|
+
through_scope)
|
17
|
+
|
18
|
+
through_records = owners.map do |owner|
|
19
|
+
association = owner.association through_reflection.name
|
20
|
+
|
21
|
+
[owner, Array(association.reader)]
|
22
|
+
end
|
23
|
+
|
24
|
+
reset_association owners, through_reflection.name
|
25
|
+
|
26
|
+
middle_records = through_records.flat_map { |(_,rec)| rec }
|
27
|
+
|
28
|
+
preloaders = preloader.preload(middle_records,
|
29
|
+
source_reflection.name,
|
30
|
+
reflection_scope)
|
31
|
+
|
32
|
+
@preloaded_records = preloaders.flat_map(&:preloaded_records)
|
33
|
+
|
34
|
+
middle_to_pl = preloaders.each_with_object({}) do |pl,h|
|
35
|
+
pl.owners.each { |middle|
|
36
|
+
h[middle] = pl
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
record_offset = {}
|
41
|
+
@preloaded_records.each_with_index do |record,i|
|
42
|
+
record_offset[record] = i
|
43
|
+
end
|
44
|
+
|
45
|
+
through_records.each_with_object({}) { |(lhs,center),records_by_owner|
|
46
|
+
pl_to_middle = center.group_by { |record| middle_to_pl[record] }
|
47
|
+
|
48
|
+
records_by_owner[lhs] = pl_to_middle.flat_map do |pl, middles|
|
49
|
+
rhs_records = middles.flat_map { |r|
|
50
|
+
association = r.association source_reflection.name
|
51
|
+
|
52
|
+
association.reader
|
53
|
+
}.compact
|
54
|
+
|
55
|
+
rhs_records.sort_by { |rhs| record_offset[rhs] }
|
56
|
+
end
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def reset_association(owners, association_name)
|
63
|
+
should_reset = (through_scope != through_reflection.klass.unscoped) ||
|
64
|
+
(reflection.options[:source_type] && through_reflection.collection?)
|
65
|
+
|
66
|
+
# Don't cache the association - we would only be caching a subset
|
67
|
+
if should_reset
|
68
|
+
owners.each { |owner|
|
69
|
+
owner.association(association_name).reset
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def through_scope
|
76
|
+
scope = through_reflection.klass.unscoped
|
77
|
+
|
78
|
+
if options[:source_type]
|
79
|
+
scope.where! reflection.foreign_type => options[:source_type]
|
80
|
+
else
|
81
|
+
unless reflection_scope.where_values.empty?
|
82
|
+
scope.includes_values = Array(reflection_scope.values[:includes] || options[:source])
|
83
|
+
scope.where_values = reflection_scope.values[:where]
|
84
|
+
scope.bind_values = reflection_scope.bind_values
|
85
|
+
end
|
86
|
+
|
87
|
+
scope.references! reflection_scope.values[:references]
|
88
|
+
scope = scope.order reflection_scope.values[:order] if scope.eager_loading?
|
89
|
+
end
|
90
|
+
|
91
|
+
scope
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Associations
|
3
|
+
class SingularAssociation < Association #:nodoc:
|
4
|
+
# Implements the reader method, e.g. foo.bar for Foo.has_one :bar
|
5
|
+
def reader(force_reload = false)
|
6
|
+
if force_reload
|
7
|
+
klass.uncached { reload }
|
8
|
+
elsif !loaded? || stale_target?
|
9
|
+
reload
|
10
|
+
end
|
11
|
+
|
12
|
+
target
|
13
|
+
end
|
14
|
+
|
15
|
+
# Implements the writer method, e.g. foo.bar= for Foo.belongs_to :bar
|
16
|
+
def writer(record)
|
17
|
+
replace(record)
|
18
|
+
end
|
19
|
+
|
20
|
+
def create(attributes = {}, &block)
|
21
|
+
_create_record(attributes, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def create!(attributes = {}, &block)
|
25
|
+
_create_record(attributes, true, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def build(attributes = {})
|
29
|
+
record = build_record(attributes)
|
30
|
+
yield(record) if block_given?
|
31
|
+
set_new_record(record)
|
32
|
+
record
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def create_scope
|
38
|
+
scope.scope_for_create.stringify_keys.except(klass.primary_key)
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_records
|
42
|
+
if reflection.scope_chain.any?(&:any?) ||
|
43
|
+
scope.eager_loading? ||
|
44
|
+
klass.current_scope ||
|
45
|
+
klass.default_scopes.any?
|
46
|
+
|
47
|
+
return scope.limit(1).to_a
|
48
|
+
end
|
49
|
+
|
50
|
+
conn = klass.connection
|
51
|
+
sc = reflection.association_scope_cache(conn, owner) do
|
52
|
+
StatementCache.create(conn) { |params|
|
53
|
+
as = AssociationScope.create { params.bind }
|
54
|
+
target_scope.merge(as.scope(self, conn)).limit(1)
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
59
|
+
sc.execute binds, klass, klass.connection
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_target
|
63
|
+
if record = get_records.first
|
64
|
+
set_inverse_instance record
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def replace(record)
|
69
|
+
raise NotImplementedError, "Subclasses must implement a replace(record) method"
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_new_record(record)
|
73
|
+
replace(record)
|
74
|
+
end
|
75
|
+
|
76
|
+
def _create_record(attributes, raise_error = false)
|
77
|
+
record = build_record(attributes)
|
78
|
+
yield(record) if block_given?
|
79
|
+
saved = record.save
|
80
|
+
set_new_record(record)
|
81
|
+
raise RecordInvalid.new(record) if !saved && raise_error
|
82
|
+
record
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# = Active Record Through Association
|
3
|
+
module Associations
|
4
|
+
module ThroughAssociation #:nodoc:
|
5
|
+
|
6
|
+
delegate :source_reflection, :through_reflection, :to => :reflection
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
# We merge in these scopes for two reasons:
|
11
|
+
#
|
12
|
+
# 1. To get the default_scope conditions for any of the other reflections in the chain
|
13
|
+
# 2. To get the type conditions for any STI models in the chain
|
14
|
+
def target_scope
|
15
|
+
scope = super
|
16
|
+
reflection.chain.drop(1).each do |reflection|
|
17
|
+
relation = reflection.klass.all
|
18
|
+
|
19
|
+
reflection_scope = reflection.scope
|
20
|
+
if reflection_scope && reflection_scope.arity.zero?
|
21
|
+
relation.merge!(reflection_scope)
|
22
|
+
end
|
23
|
+
|
24
|
+
scope.merge!(
|
25
|
+
relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
scope
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Construct attributes for :through pointing to owner and associate. This is used by the
|
34
|
+
# methods which create and delete records on the association.
|
35
|
+
#
|
36
|
+
# We only support indirectly modifying through associations which has a belongs_to source.
|
37
|
+
# This is the "has_many :tags, through: :taggings" situation, where the join model
|
38
|
+
# typically has a belongs_to on both side. In other words, associations which could also
|
39
|
+
# be represented as has_and_belongs_to_many associations.
|
40
|
+
#
|
41
|
+
# We do not support creating/deleting records on the association where the source has
|
42
|
+
# some other type, because this opens up a whole can of worms, and in basically any
|
43
|
+
# situation it is more natural for the user to just create or modify their join records
|
44
|
+
# directly as required.
|
45
|
+
def construct_join_attributes(*records)
|
46
|
+
ensure_mutable
|
47
|
+
|
48
|
+
if source_reflection.association_primary_key(reflection.klass) == reflection.klass.primary_key
|
49
|
+
join_attributes = { source_reflection.name => records }
|
50
|
+
else
|
51
|
+
join_attributes = {
|
52
|
+
source_reflection.foreign_key =>
|
53
|
+
records.map { |record|
|
54
|
+
record.send(source_reflection.association_primary_key(reflection.klass))
|
55
|
+
}
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
if options[:source_type]
|
60
|
+
join_attributes[source_reflection.foreign_type] =
|
61
|
+
records.map { |record| record.class.base_class.name }
|
62
|
+
end
|
63
|
+
|
64
|
+
if records.count == 1
|
65
|
+
Hash[join_attributes.map { |k, v| [k, v.first] }]
|
66
|
+
else
|
67
|
+
join_attributes
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Note: this does not capture all cases, for example it would be crazy to try to
|
72
|
+
# properly support stale-checking for nested associations.
|
73
|
+
def stale_state
|
74
|
+
if through_reflection.belongs_to?
|
75
|
+
owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def foreign_key_present?
|
80
|
+
through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil?
|
81
|
+
end
|
82
|
+
|
83
|
+
def ensure_mutable
|
84
|
+
unless source_reflection.belongs_to?
|
85
|
+
raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def ensure_not_nested
|
90
|
+
if reflection.nested?
|
91
|
+
raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Attribute # :nodoc:
|
3
|
+
class << self
|
4
|
+
def from_database(name, value, type)
|
5
|
+
FromDatabase.new(name, value, type)
|
6
|
+
end
|
7
|
+
|
8
|
+
def from_user(name, value, type)
|
9
|
+
FromUser.new(name, value, type)
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_cast_value(name, value, type)
|
13
|
+
WithCastValue.new(name, value, type)
|
14
|
+
end
|
15
|
+
|
16
|
+
def null(name)
|
17
|
+
Null.new(name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def uninitialized(name, type)
|
21
|
+
Uninitialized.new(name, type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :name, :value_before_type_cast, :type
|
26
|
+
|
27
|
+
# This method should not be called directly.
|
28
|
+
# Use #from_database or #from_user
|
29
|
+
def initialize(name, value_before_type_cast, type)
|
30
|
+
@name = name
|
31
|
+
@value_before_type_cast = value_before_type_cast
|
32
|
+
@type = type
|
33
|
+
end
|
34
|
+
|
35
|
+
def value
|
36
|
+
# `defined?` is cheaper than `||=` when we get back falsy values
|
37
|
+
@value = original_value unless defined?(@value)
|
38
|
+
@value
|
39
|
+
end
|
40
|
+
|
41
|
+
def original_value
|
42
|
+
type_cast(value_before_type_cast)
|
43
|
+
end
|
44
|
+
|
45
|
+
def value_for_database
|
46
|
+
type.type_cast_for_database(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def changed_from?(old_value)
|
50
|
+
type.changed?(old_value, value, value_before_type_cast)
|
51
|
+
end
|
52
|
+
|
53
|
+
def changed_in_place_from?(old_value)
|
54
|
+
type.changed_in_place?(old_value, value)
|
55
|
+
end
|
56
|
+
|
57
|
+
def with_value_from_user(value)
|
58
|
+
self.class.from_user(name, value, type)
|
59
|
+
end
|
60
|
+
|
61
|
+
def with_value_from_database(value)
|
62
|
+
self.class.from_database(name, value, type)
|
63
|
+
end
|
64
|
+
|
65
|
+
def with_cast_value(value)
|
66
|
+
self.class.with_cast_value(name, value, type)
|
67
|
+
end
|
68
|
+
|
69
|
+
def type_cast(*)
|
70
|
+
raise NotImplementedError
|
71
|
+
end
|
72
|
+
|
73
|
+
def initialized?
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
def ==(other)
|
78
|
+
self.class == other.class &&
|
79
|
+
name == other.name &&
|
80
|
+
value_before_type_cast == other.value_before_type_cast &&
|
81
|
+
type == other.type
|
82
|
+
end
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
def initialize_dup(other)
|
87
|
+
if defined?(@value) && @value.duplicable?
|
88
|
+
@value = @value.dup
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class FromDatabase < Attribute # :nodoc:
|
93
|
+
def type_cast(value)
|
94
|
+
type.type_cast_from_database(value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class FromUser < Attribute # :nodoc:
|
99
|
+
def type_cast(value)
|
100
|
+
type.type_cast_from_user(value)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class WithCastValue < Attribute # :nodoc:
|
105
|
+
def type_cast(value)
|
106
|
+
value
|
107
|
+
end
|
108
|
+
|
109
|
+
def changed_in_place_from?(old_value)
|
110
|
+
false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class Null < Attribute # :nodoc:
|
115
|
+
def initialize(name)
|
116
|
+
super(name, nil, Type::Value.new)
|
117
|
+
end
|
118
|
+
|
119
|
+
def value
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def with_value_from_database(value)
|
124
|
+
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
|
125
|
+
end
|
126
|
+
alias_method :with_value_from_user, :with_value_from_database
|
127
|
+
end
|
128
|
+
|
129
|
+
class Uninitialized < Attribute # :nodoc:
|
130
|
+
def initialize(name, type)
|
131
|
+
super(name, nil, type)
|
132
|
+
end
|
133
|
+
|
134
|
+
def value
|
135
|
+
if block_given?
|
136
|
+
yield name
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def value_for_database
|
141
|
+
end
|
142
|
+
|
143
|
+
def initialized?
|
144
|
+
false
|
145
|
+
end
|
146
|
+
end
|
147
|
+
private_constant :FromDatabase, :FromUser, :Null, :Uninitialized
|
148
|
+
end
|
149
|
+
end
|