activerecord 4.1.16 → 4.2.0.beta1
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 +4 -4
- data/CHANGELOG.md +634 -2185
- data/README.rdoc +15 -10
- data/lib/active_record.rb +2 -1
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/attribute.rb +131 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -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 +76 -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 +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -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 +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- data/lib/active_record/migration.rb +22 -32
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -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/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -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 +51 -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 +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record/validations/uniqueness.rb +9 -23
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +71 -14
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -31,6 +31,14 @@ module ActiveRecord
|
|
31
31
|
@updated
|
32
32
|
end
|
33
33
|
|
34
|
+
def decrement_counters # :nodoc:
|
35
|
+
with_cache_name { |name| decrement_counter name }
|
36
|
+
end
|
37
|
+
|
38
|
+
def increment_counters # :nodoc:
|
39
|
+
with_cache_name { |name| increment_counter name }
|
40
|
+
end
|
41
|
+
|
34
42
|
private
|
35
43
|
|
36
44
|
def find_target?
|
@@ -51,13 +59,15 @@ module ActiveRecord
|
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
54
|
-
def
|
55
|
-
|
62
|
+
def decrement_counter(counter_cache_name)
|
63
|
+
if foreign_key_present?
|
64
|
+
klass.decrement_counter(counter_cache_name, target_id)
|
65
|
+
end
|
56
66
|
end
|
57
67
|
|
58
|
-
def
|
68
|
+
def increment_counter(counter_cache_name)
|
59
69
|
if foreign_key_present?
|
60
|
-
klass.
|
70
|
+
klass.increment_counter(counter_cache_name, target_id)
|
61
71
|
end
|
62
72
|
end
|
63
73
|
|
@@ -82,7 +92,7 @@ module ActiveRecord
|
|
82
92
|
# has_one associations.
|
83
93
|
def invertible_for?(record)
|
84
94
|
inverse = inverse_reflection_for(record)
|
85
|
-
inverse && inverse.
|
95
|
+
inverse && inverse.has_one?
|
86
96
|
end
|
87
97
|
|
88
98
|
def target_id
|
@@ -21,7 +21,7 @@ module ActiveRecord::Associations::Builder
|
|
21
21
|
end
|
22
22
|
self.extensions = []
|
23
23
|
|
24
|
-
self.valid_options = [:class_name, :
|
24
|
+
self.valid_options = [:class_name, :class, :foreign_key, :validate]
|
25
25
|
|
26
26
|
attr_reader :name, :scope, :options
|
27
27
|
|
@@ -36,6 +36,7 @@ module ActiveRecord::Associations::Builder
|
|
36
36
|
reflection = builder.build(model)
|
37
37
|
define_accessors model, reflection
|
38
38
|
define_callbacks model, reflection
|
39
|
+
define_validations model, reflection
|
39
40
|
builder.define_extensions model
|
40
41
|
reflection
|
41
42
|
end
|
@@ -85,7 +86,11 @@ module ActiveRecord::Associations::Builder
|
|
85
86
|
end
|
86
87
|
|
87
88
|
def self.define_callbacks(model, reflection)
|
88
|
-
|
89
|
+
if dependent = reflection.options[:dependent]
|
90
|
+
check_dependent_options(dependent)
|
91
|
+
add_destroy_callbacks(model, reflection)
|
92
|
+
end
|
93
|
+
|
89
94
|
Association.extensions.each do |extension|
|
90
95
|
extension.build model, reflection
|
91
96
|
end
|
@@ -120,17 +125,23 @@ module ActiveRecord::Associations::Builder
|
|
120
125
|
CODE
|
121
126
|
end
|
122
127
|
|
128
|
+
def self.define_validations(model, reflection)
|
129
|
+
# noop
|
130
|
+
end
|
131
|
+
|
123
132
|
def self.valid_dependent_options
|
124
133
|
raise NotImplementedError
|
125
134
|
end
|
126
135
|
|
127
136
|
private
|
128
137
|
|
129
|
-
def self.
|
130
|
-
unless valid_dependent_options.include?
|
131
|
-
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{
|
138
|
+
def self.check_dependent_options(dependent)
|
139
|
+
unless valid_dependent_options.include? dependent
|
140
|
+
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
|
132
141
|
end
|
142
|
+
end
|
133
143
|
|
144
|
+
def self.add_destroy_callbacks(model, reflection)
|
134
145
|
name = reflection.name
|
135
146
|
model.before_destroy lambda { |o| o.association(name).handle_dependency }
|
136
147
|
end
|
@@ -26,28 +26,9 @@ module ActiveRecord::Associations::Builder
|
|
26
26
|
private
|
27
27
|
|
28
28
|
def self.add_counter_cache_methods(mixin)
|
29
|
-
return if mixin.method_defined? :
|
29
|
+
return if mixin.method_defined? :belongs_to_counter_cache_after_update
|
30
30
|
|
31
31
|
mixin.class_eval do
|
32
|
-
def belongs_to_counter_cache_after_create(reflection)
|
33
|
-
if record = send(reflection.name)
|
34
|
-
cache_column = reflection.counter_cache_column
|
35
|
-
record.class.increment_counter(cache_column, record.id)
|
36
|
-
@_after_create_counter_called = true
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def belongs_to_counter_cache_before_destroy(reflection)
|
41
|
-
foreign_key = reflection.foreign_key.to_sym
|
42
|
-
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
|
43
|
-
record = send reflection.name
|
44
|
-
if record && !self.destroyed?
|
45
|
-
cache_column = reflection.counter_cache_column
|
46
|
-
record.class.decrement_counter(cache_column, record.id)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
32
|
def belongs_to_counter_cache_after_update(reflection)
|
52
33
|
foreign_key = reflection.foreign_key
|
53
34
|
cache_column = reflection.counter_cache_column
|
@@ -73,14 +54,6 @@ module ActiveRecord::Associations::Builder
|
|
73
54
|
def self.add_counter_cache_callbacks(model, reflection)
|
74
55
|
cache_column = reflection.counter_cache_column
|
75
56
|
|
76
|
-
model.after_create lambda { |record|
|
77
|
-
record.belongs_to_counter_cache_after_create(reflection)
|
78
|
-
}
|
79
|
-
|
80
|
-
model.before_destroy lambda { |record|
|
81
|
-
record.belongs_to_counter_cache_before_destroy(reflection)
|
82
|
-
}
|
83
|
-
|
84
57
|
model.after_update lambda { |record|
|
85
58
|
record.belongs_to_counter_cache_after_update(reflection)
|
86
59
|
}
|
@@ -130,9 +103,14 @@ module ActiveRecord::Associations::Builder
|
|
130
103
|
BelongsTo.touch_record(record, foreign_key, n, touch)
|
131
104
|
}
|
132
105
|
|
133
|
-
model.after_save callback
|
106
|
+
model.after_save callback, if: :changed?
|
134
107
|
model.after_touch callback
|
135
108
|
model.after_destroy callback
|
136
109
|
end
|
110
|
+
|
111
|
+
def self.add_destroy_callbacks(model, reflection)
|
112
|
+
name = reflection.name
|
113
|
+
model.after_destroy lambda { |o| o.association(name).handle_dependency }
|
114
|
+
end
|
137
115
|
end
|
138
116
|
end
|
@@ -72,22 +72,13 @@ module ActiveRecord::Associations::Builder
|
|
72
72
|
self.right_reflection = _reflect_on_association(rhs_name)
|
73
73
|
end
|
74
74
|
|
75
|
-
def hash
|
76
|
-
object_id.hash
|
77
|
-
end
|
78
|
-
|
79
|
-
def ==(other)
|
80
|
-
equal?(other)
|
81
|
-
end
|
82
|
-
alias :eql? :==
|
83
|
-
|
84
75
|
}
|
85
76
|
|
86
77
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
87
78
|
join_model.table_name_resolver = habtm
|
88
79
|
join_model.class_resolver = lhs_model
|
89
80
|
|
90
|
-
join_model.add_left_association :left_side,
|
81
|
+
join_model.add_left_association :left_side, class: lhs_model
|
91
82
|
join_model.add_right_association association_name, belongs_to_options(options)
|
92
83
|
join_model
|
93
84
|
end
|
@@ -107,7 +98,7 @@ module ActiveRecord::Associations::Builder
|
|
107
98
|
|
108
99
|
def middle_options(join_model)
|
109
100
|
middle_options = {}
|
110
|
-
middle_options[:
|
101
|
+
middle_options[:class] = join_model
|
111
102
|
middle_options[:source] = join_model.left_reflection.name
|
112
103
|
if options.key? :foreign_key
|
113
104
|
middle_options[:foreign_key] = options[:foreign_key]
|
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def valid_options
|
8
|
-
valid = super + [:
|
8
|
+
valid = super + [:as]
|
9
9
|
valid += [:through, :source, :source_type] if options[:through]
|
10
10
|
valid
|
11
11
|
end
|
@@ -16,7 +16,7 @@ module ActiveRecord::Associations::Builder
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
def self.
|
19
|
+
def self.add_destroy_callbacks(model, reflection)
|
20
20
|
super unless reflection.options[:through]
|
21
21
|
end
|
22
22
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveRecord::Associations::Builder
|
4
4
|
class SingularAssociation < Association #:nodoc:
|
5
5
|
def valid_options
|
6
|
-
super + [:
|
6
|
+
super + [:dependent, :primary_key, :inverse_of, :required]
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.define_accessors(model, reflection)
|
@@ -27,5 +27,12 @@ module ActiveRecord::Associations::Builder
|
|
27
27
|
end
|
28
28
|
CODE
|
29
29
|
end
|
30
|
+
|
31
|
+
def self.define_validations(model, reflection)
|
32
|
+
super
|
33
|
+
if reflection.options[:required]
|
34
|
+
model.validates_presence_of reflection.name
|
35
|
+
end
|
36
|
+
end
|
30
37
|
end
|
31
38
|
end
|
@@ -33,13 +33,7 @@ module ActiveRecord
|
|
33
33
|
reload
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
# Cache the proxy separately before the owner has an id
|
38
|
-
# or else a post-save proxy will still lack the id
|
39
|
-
@new_record_proxy ||= CollectionProxy.create(klass, self)
|
40
|
-
else
|
41
|
-
@proxy ||= CollectionProxy.create(klass, self)
|
42
|
-
end
|
36
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
43
37
|
end
|
44
38
|
|
45
39
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -61,9 +55,9 @@ module ActiveRecord
|
|
61
55
|
|
62
56
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
63
57
|
def ids_writer(ids)
|
64
|
-
|
58
|
+
pk_type = reflection.primary_key_type
|
65
59
|
ids = Array(ids).reject { |id| id.blank? }
|
66
|
-
ids.map! { |i|
|
60
|
+
ids.map! { |i| pk_type.type_cast_from_user(i) }
|
67
61
|
replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids))
|
68
62
|
end
|
69
63
|
|
@@ -129,16 +123,6 @@ module ActiveRecord
|
|
129
123
|
first_nth_or_last(:last, *args)
|
130
124
|
end
|
131
125
|
|
132
|
-
def take(n = nil)
|
133
|
-
if loaded?
|
134
|
-
n ? target.take(n) : target.first
|
135
|
-
else
|
136
|
-
scope.take(n).tap do |record|
|
137
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
126
|
def build(attributes = {}, &block)
|
143
127
|
if attributes.is_a?(Array)
|
144
128
|
attributes.collect { |attr| build(attr, &block) }
|
@@ -161,9 +145,8 @@ module ActiveRecord
|
|
161
145
|
# be chained. Since << flattens its argument list and inserts each record,
|
162
146
|
# +push+ and +concat+ behave identically.
|
163
147
|
def concat(*records)
|
164
|
-
load_target if owner.new_record?
|
165
|
-
|
166
148
|
if owner.new_record?
|
149
|
+
load_target
|
167
150
|
concat_records(records)
|
168
151
|
else
|
169
152
|
transaction { concat_records(records) }
|
@@ -199,11 +182,11 @@ module ActiveRecord
|
|
199
182
|
#
|
200
183
|
# See delete for more info.
|
201
184
|
def delete_all(dependent = nil)
|
202
|
-
if dependent
|
185
|
+
if dependent && ![:nullify, :delete_all].include?(dependent)
|
203
186
|
raise ArgumentError, "Valid values are :nullify or :delete_all"
|
204
187
|
end
|
205
188
|
|
206
|
-
dependent = if dependent
|
189
|
+
dependent = if dependent
|
207
190
|
dependent
|
208
191
|
elsif options[:dependent] == :destroy
|
209
192
|
:delete_all
|
@@ -211,7 +194,7 @@ module ActiveRecord
|
|
211
194
|
options[:dependent]
|
212
195
|
end
|
213
196
|
|
214
|
-
|
197
|
+
delete_or_nullify_all_records(dependent).tap do
|
215
198
|
reset
|
216
199
|
loaded!
|
217
200
|
end
|
@@ -261,19 +244,12 @@ module ActiveRecord
|
|
261
244
|
# are actually removed from the database, that depends precisely on
|
262
245
|
# +delete_records+. They are in any case removed from the collection.
|
263
246
|
def delete(*records)
|
247
|
+
return if records.empty?
|
264
248
|
_options = records.extract_options!
|
265
249
|
dependent = _options[:dependent] || options[:dependent]
|
266
250
|
|
267
|
-
if records.
|
268
|
-
|
269
|
-
delete_or_destroy(load_target, dependent)
|
270
|
-
else
|
271
|
-
delete_records(:all, dependent)
|
272
|
-
end
|
273
|
-
else
|
274
|
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
275
|
-
delete_or_destroy(records, dependent)
|
276
|
-
end
|
251
|
+
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
252
|
+
delete_or_destroy(records, dependent)
|
277
253
|
end
|
278
254
|
|
279
255
|
# Deletes the +records+ and removes them from this association calling
|
@@ -282,6 +258,7 @@ module ActiveRecord
|
|
282
258
|
# Note that this method removes records from the database ignoring the
|
283
259
|
# +:dependent+ option.
|
284
260
|
def destroy(*records)
|
261
|
+
return if records.empty?
|
285
262
|
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
286
263
|
delete_or_destroy(records, :destroy)
|
287
264
|
end
|
@@ -375,7 +352,9 @@ module ActiveRecord
|
|
375
352
|
if owner.new_record?
|
376
353
|
replace_records(other_array, original_target)
|
377
354
|
else
|
378
|
-
|
355
|
+
if other_array != original_target
|
356
|
+
transaction { replace_records(other_array, original_target) }
|
357
|
+
end
|
379
358
|
end
|
380
359
|
end
|
381
360
|
|
@@ -384,7 +363,7 @@ module ActiveRecord
|
|
384
363
|
if record.new_record?
|
385
364
|
include_in_memory?(record)
|
386
365
|
else
|
387
|
-
loaded? ? target.include?(record) : scope.exists?(record)
|
366
|
+
loaded? ? target.include?(record) : scope.exists?(record.id)
|
388
367
|
end
|
389
368
|
else
|
390
369
|
false
|
@@ -427,9 +406,23 @@ module ActiveRecord
|
|
427
406
|
end
|
428
407
|
|
429
408
|
private
|
409
|
+
def get_records
|
410
|
+
return scope.to_a if reflection.scope_chain.any?(&:any?)
|
411
|
+
|
412
|
+
conn = klass.connection
|
413
|
+
sc = reflection.association_scope_cache(conn, owner) do
|
414
|
+
StatementCache.create(conn) { |params|
|
415
|
+
as = AssociationScope.create { params.bind }
|
416
|
+
target_scope.merge as.scope(self, conn)
|
417
|
+
}
|
418
|
+
end
|
419
|
+
|
420
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
421
|
+
sc.execute binds, klass, klass.connection
|
422
|
+
end
|
430
423
|
|
431
424
|
def find_target
|
432
|
-
records =
|
425
|
+
records = get_records
|
433
426
|
records.each { |record| set_inverse_instance(record) }
|
434
427
|
records
|
435
428
|
end
|
@@ -576,13 +569,8 @@ module ActiveRecord
|
|
576
569
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
577
570
|
assoc = owner.association(reflection.through_reflection.name)
|
578
571
|
assoc.reader.any? { |source|
|
579
|
-
|
580
|
-
|
581
|
-
if target_association.respond_to?(:include?)
|
582
|
-
target_association.include?(record)
|
583
|
-
else
|
584
|
-
target_association == record
|
585
|
-
end
|
572
|
+
target = source.send(reflection.source_reflection.name)
|
573
|
+
target.respond_to?(:include?) ? target.include?(record) : target == record
|
586
574
|
} || target.include?(record)
|
587
575
|
else
|
588
576
|
target.include?(record)
|
@@ -226,10 +226,6 @@ module ActiveRecord
|
|
226
226
|
@association.last(*args)
|
227
227
|
end
|
228
228
|
|
229
|
-
def take(n = nil)
|
230
|
-
@association.take(n)
|
231
|
-
end
|
232
|
-
|
233
229
|
# Returns a new object of the collection type that has been instantiated
|
234
230
|
# with +attributes+ and linked to this object, but have not yet been saved.
|
235
231
|
# You can pass an array of attributes hashes, this will return an array
|
@@ -361,7 +357,7 @@ module ActiveRecord
|
|
361
357
|
|
362
358
|
# Deletes all the records from the collection. For +has_many+ associations,
|
363
359
|
# the deletion is done according to the strategy specified by the <tt>:dependent</tt>
|
364
|
-
# option.
|
360
|
+
# option.
|
365
361
|
#
|
366
362
|
# If no <tt>:dependent</tt> option is given, then it will follow the
|
367
363
|
# default strategy. The default strategy is <tt>:nullify</tt>. This
|
@@ -439,11 +435,6 @@ module ActiveRecord
|
|
439
435
|
# # ]
|
440
436
|
#
|
441
437
|
# person.pets.delete_all
|
442
|
-
# # => [
|
443
|
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
444
|
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
445
|
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
446
|
-
# # ]
|
447
438
|
#
|
448
439
|
# Pet.find(1, 2, 3)
|
449
440
|
# # => ActiveRecord::RecordNotFound
|
@@ -41,6 +41,14 @@ module ActiveRecord
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
def empty?
|
45
|
+
if has_cached_counter?
|
46
|
+
size.zero?
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
44
52
|
private
|
45
53
|
|
46
54
|
# Returns the number of records in this collection.
|
@@ -58,7 +66,7 @@ module ActiveRecord
|
|
58
66
|
# the loaded flag is set to true as well.
|
59
67
|
def count_records
|
60
68
|
count = if has_cached_counter?
|
61
|
-
owner.
|
69
|
+
owner.read_attribute cached_counter_attribute_name
|
62
70
|
else
|
63
71
|
scope.count
|
64
72
|
end
|
@@ -80,11 +88,22 @@ module ActiveRecord
|
|
80
88
|
end
|
81
89
|
|
82
90
|
def update_counter(difference, reflection = reflection())
|
91
|
+
update_counter_in_database(difference, reflection)
|
92
|
+
update_counter_in_memory(difference, reflection)
|
93
|
+
end
|
94
|
+
|
95
|
+
def update_counter_in_database(difference, reflection = reflection())
|
83
96
|
if has_cached_counter?(reflection)
|
84
97
|
counter = cached_counter_attribute_name(reflection)
|
85
98
|
owner.class.update_counters(owner.id, counter => difference)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def update_counter_in_memory(difference, reflection = reflection())
|
103
|
+
if has_cached_counter?(reflection)
|
104
|
+
counter = cached_counter_attribute_name(reflection)
|
86
105
|
owner[counter] += difference
|
87
|
-
owner.
|
106
|
+
owner.send(:clear_attribute_changes, counter) # eww
|
88
107
|
end
|
89
108
|
end
|
90
109
|
|
@@ -100,29 +119,37 @@ module ActiveRecord
|
|
100
119
|
# Hence this method.
|
101
120
|
def inverse_updates_counter_cache?(reflection = reflection())
|
102
121
|
counter_name = cached_counter_attribute_name(reflection)
|
122
|
+
inverse_updates_counter_named?(counter_name, reflection)
|
123
|
+
end
|
124
|
+
|
125
|
+
def inverse_updates_counter_named?(counter_name, reflection = reflection())
|
103
126
|
reflection.klass._reflections.values.any? { |inverse_reflection|
|
104
|
-
|
127
|
+
inverse_reflection.belongs_to? &&
|
105
128
|
inverse_reflection.counter_cache_column == counter_name
|
106
129
|
}
|
107
130
|
end
|
108
131
|
|
132
|
+
def delete_count(method, scope)
|
133
|
+
if method == :delete_all
|
134
|
+
scope.delete_all
|
135
|
+
else
|
136
|
+
scope.update_all(reflection.foreign_key => nil)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def delete_or_nullify_all_records(method)
|
141
|
+
count = delete_count(method, self.scope)
|
142
|
+
update_counter(-count)
|
143
|
+
end
|
144
|
+
|
109
145
|
# Deletes the records according to the <tt>:dependent</tt> option.
|
110
146
|
def delete_records(records, method)
|
111
147
|
if method == :destroy
|
112
148
|
records.each(&:destroy!)
|
113
149
|
update_counter(-records.length) unless inverse_updates_counter_cache?
|
114
150
|
else
|
115
|
-
|
116
|
-
|
117
|
-
else
|
118
|
-
scope = self.scope.where(reflection.klass.primary_key => records)
|
119
|
-
end
|
120
|
-
|
121
|
-
if method == :delete_all
|
122
|
-
update_counter(-scope.delete_all)
|
123
|
-
else
|
124
|
-
update_counter(-scope.update_all(reflection.foreign_key => nil))
|
125
|
-
end
|
151
|
+
scope = self.scope.where(reflection.klass.primary_key => records)
|
152
|
+
update_counter(-delete_count(method, scope))
|
126
153
|
end
|
127
154
|
end
|
128
155
|
|
@@ -133,6 +160,25 @@ module ActiveRecord
|
|
133
160
|
false
|
134
161
|
end
|
135
162
|
end
|
163
|
+
|
164
|
+
def concat_records(records, *)
|
165
|
+
update_counter_if_success(super, records.length)
|
166
|
+
end
|
167
|
+
|
168
|
+
def _create_record(attributes, *)
|
169
|
+
if attributes.is_a?(Array)
|
170
|
+
super
|
171
|
+
else
|
172
|
+
update_counter_if_success(super, 1)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def update_counter_if_success(saved_successfully, difference)
|
177
|
+
if saved_successfully
|
178
|
+
update_counter_in_memory(difference)
|
179
|
+
end
|
180
|
+
saved_successfully
|
181
|
+
end
|
136
182
|
end
|
137
183
|
end
|
138
184
|
end
|