activerecord 3.2.22.4 → 4.0.13
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 +2799 -617
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -32
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations/alias_tracker.rb +4 -2
- data/lib/active_record/associations/association.rb +60 -46
- data/lib/active_record/associations/association_scope.rb +46 -40
- data/lib/active_record/associations/belongs_to_association.rb +17 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +73 -56
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +130 -96
- data/lib/active_record/associations/collection_proxy.rb +916 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
- data/lib/active_record/associations/has_many_association.rb +35 -8
- data/lib/active_record/associations/has_many_through_association.rb +37 -17
- data/lib/active_record/associations/has_one_association.rb +42 -19
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
- data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
- data/lib/active_record/associations/join_dependency.rb +30 -9
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/preloader.rb +20 -43
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +223 -282
- data/lib/active_record/attribute_assignment.rb +134 -154
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +36 -29
- data/lib/active_record/attribute_methods/primary_key.rb +45 -31
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +67 -90
- data/lib/active_record/attribute_methods/serialization.rb +133 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
- data/lib/active_record/attribute_methods/write.rb +34 -39
- data/lib/active_record/attribute_methods.rb +268 -108
- data/lib/active_record/autosave_association.rb +80 -73
- data/lib/active_record/base.rb +54 -451
- data/lib/active_record/callbacks.rb +60 -22
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
- data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
- data/lib/active_record/connection_adapters/column.rb +67 -36
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
- data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
- data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +472 -0
- data/lib/active_record/counter_cache.rb +107 -108
- data/lib/active_record/dynamic_matchers.rb +115 -63
- data/lib/active_record/errors.rb +36 -18
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +8 -4
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +159 -155
- data/lib/active_record/inheritance.rb +93 -59
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +39 -43
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration/command_recorder.rb +102 -33
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +411 -173
- data/lib/active_record/model_schema.rb +81 -94
- data/lib/active_record/nested_attributes.rb +173 -131
- data/lib/active_record/null_relation.rb +67 -0
- data/lib/active_record/persistence.rb +254 -106
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +113 -38
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +115 -368
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +110 -61
- data/lib/active_record/relation/batches.rb +29 -29
- data/lib/active_record/relation/calculations.rb +155 -125
- data/lib/active_record/relation/delegation.rb +94 -18
- data/lib/active_record/relation/finder_methods.rb +151 -203
- data/lib/active_record/relation/merger.rb +188 -0
- data/lib/active_record/relation/predicate_builder.rb +85 -42
- data/lib/active_record/relation/query_methods.rb +793 -146
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/relation.rb +293 -173
- data/lib/active_record/result.rb +48 -7
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +41 -54
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +41 -41
- data/lib/active_record/schema_migration.rb +46 -0
- data/lib/active_record/scoping/default.rb +56 -52
- data/lib/active_record/scoping/named.rb +78 -103
- data/lib/active_record/scoping.rb +54 -124
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +131 -15
- data/lib/active_record/tasks/database_tasks.rb +204 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +67 -38
- data/lib/active_record/timestamp.rb +16 -11
- data/lib/active_record/transactions.rb +73 -51
- data/lib/active_record/validations/associated.rb +19 -13
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +110 -57
- data/lib/active_record/validations.rb +18 -17
- data/lib/active_record/version.rb +7 -6
- data/lib/active_record.rb +63 -45
- data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -5
- metadata +43 -29
- data/examples/associations.png +0 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/array/wrap'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
module Associations
|
5
3
|
# = Active Record Association Collection
|
@@ -8,6 +6,15 @@ module ActiveRecord
|
|
8
6
|
# ease the implementation of association proxies that represent
|
9
7
|
# collections. See the class hierarchy in AssociationProxy.
|
10
8
|
#
|
9
|
+
# CollectionAssociation:
|
10
|
+
# HasAndBelongsToManyAssociation => has_and_belongs_to_many
|
11
|
+
# HasManyAssociation => has_many
|
12
|
+
# HasManyThroughAssociation + ThroughAssociation => has_many :through
|
13
|
+
#
|
14
|
+
# CollectionAssociation class provides common methods to the collections
|
15
|
+
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
16
|
+
# +:through association+ option.
|
17
|
+
#
|
11
18
|
# You need to be careful with assumptions regarding the target: The proxy
|
12
19
|
# does not fetch records from the database until it needs them, but new
|
13
20
|
# ones created with +build+ are added to the target. So, the target may be
|
@@ -18,12 +25,6 @@ module ActiveRecord
|
|
18
25
|
# If you need to work on all current children, new and existing records,
|
19
26
|
# +load_target+ and the +loaded+ flag are your friends.
|
20
27
|
class CollectionAssociation < Association #:nodoc:
|
21
|
-
attr_reader :proxy
|
22
|
-
|
23
|
-
def initialize(owner, reflection)
|
24
|
-
super
|
25
|
-
@proxy = CollectionProxy.new(self)
|
26
|
-
end
|
27
28
|
|
28
29
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
29
30
|
def reader(force_reload = false)
|
@@ -33,7 +34,13 @@ module ActiveRecord
|
|
33
34
|
reload
|
34
35
|
end
|
35
36
|
|
36
|
-
|
37
|
+
if owner.new_record?
|
38
|
+
# Cache the proxy separately before the owner has an id
|
39
|
+
# or else a post-save proxy will still lack the id
|
40
|
+
@new_record_proxy ||= CollectionProxy.new(klass, self)
|
41
|
+
else
|
42
|
+
@proxy ||= CollectionProxy.new(klass, self)
|
43
|
+
end
|
37
44
|
end
|
38
45
|
|
39
46
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -43,37 +50,26 @@ module ActiveRecord
|
|
43
50
|
|
44
51
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
45
52
|
def ids_reader
|
46
|
-
if
|
53
|
+
if loaded? || options[:finder_sql]
|
47
54
|
load_target.map do |record|
|
48
55
|
record.send(reflection.association_primary_key)
|
49
56
|
end
|
50
57
|
else
|
51
58
|
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
|
52
|
-
|
53
|
-
|
54
|
-
including = (relation.eager_load_values + relation.includes_values).uniq
|
55
|
-
|
56
|
-
if including.any?
|
57
|
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(reflection.klass, including, [])
|
58
|
-
relation = join_dependency.join_associations.inject(relation) do |r, association|
|
59
|
-
association.join_relation(r)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
relation.pluck(column)
|
59
|
+
scope.pluck(column)
|
64
60
|
end
|
65
61
|
end
|
66
62
|
|
67
63
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
68
64
|
def ids_writer(ids)
|
69
65
|
pk_column = reflection.primary_key_column
|
70
|
-
ids = Array
|
66
|
+
ids = Array(ids).reject { |id| id.blank? }
|
71
67
|
ids.map! { |i| pk_column.type_cast(i) }
|
72
68
|
replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids))
|
73
69
|
end
|
74
70
|
|
75
71
|
def reset
|
76
|
-
|
72
|
+
super
|
77
73
|
@target = []
|
78
74
|
end
|
79
75
|
|
@@ -81,7 +77,7 @@ module ActiveRecord
|
|
81
77
|
if block_given?
|
82
78
|
load_target.select.each { |e| yield e }
|
83
79
|
else
|
84
|
-
|
80
|
+
scope.select(select)
|
85
81
|
end
|
86
82
|
end
|
87
83
|
|
@@ -91,8 +87,20 @@ module ActiveRecord
|
|
91
87
|
else
|
92
88
|
if options[:finder_sql]
|
93
89
|
find_by_scan(*args)
|
90
|
+
elsif options[:inverse_of] && loaded?
|
91
|
+
args_flatten = args.flatten
|
92
|
+
raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
|
93
|
+
|
94
|
+
result = find_by_scan(*args)
|
95
|
+
|
96
|
+
result_size = Array(result).size
|
97
|
+
if !result || result_size != args_flatten.size
|
98
|
+
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
99
|
+
else
|
100
|
+
result
|
101
|
+
end
|
94
102
|
else
|
95
|
-
|
103
|
+
scope.find(*args)
|
96
104
|
end
|
97
105
|
end
|
98
106
|
end
|
@@ -105,26 +113,27 @@ module ActiveRecord
|
|
105
113
|
first_or_last(:last, *args)
|
106
114
|
end
|
107
115
|
|
108
|
-
def build(attributes = {},
|
116
|
+
def build(attributes = {}, &block)
|
109
117
|
if attributes.is_a?(Array)
|
110
|
-
attributes.collect { |attr| build(attr,
|
118
|
+
attributes.collect { |attr| build(attr, &block) }
|
111
119
|
else
|
112
|
-
add_to_target(build_record(attributes
|
120
|
+
add_to_target(build_record(attributes)) do |record|
|
113
121
|
yield(record) if block_given?
|
114
122
|
end
|
115
123
|
end
|
116
124
|
end
|
117
125
|
|
118
|
-
def create(attributes = {},
|
119
|
-
|
126
|
+
def create(attributes = {}, &block)
|
127
|
+
_create_record(attributes, &block)
|
120
128
|
end
|
121
129
|
|
122
|
-
def create!(attributes = {},
|
123
|
-
|
130
|
+
def create!(attributes = {}, &block)
|
131
|
+
_create_record(attributes, true, &block)
|
124
132
|
end
|
125
133
|
|
126
|
-
# Add +records+ to this association. Returns +self+ so method calls may
|
127
|
-
# Since << flattens its argument list and inserts each record,
|
134
|
+
# Add +records+ to this association. Returns +self+ so method calls may
|
135
|
+
# be chained. Since << flattens its argument list and inserts each record,
|
136
|
+
# +push+ and +concat+ behave identically.
|
128
137
|
def concat(*records)
|
129
138
|
load_target if owner.new_record?
|
130
139
|
|
@@ -150,23 +159,16 @@ module ActiveRecord
|
|
150
159
|
end
|
151
160
|
end
|
152
161
|
|
153
|
-
# Remove all records from this association
|
162
|
+
# Remove all records from this association.
|
154
163
|
#
|
155
164
|
# See delete for more info.
|
156
165
|
def delete_all
|
157
|
-
delete(
|
166
|
+
delete(:all).tap do
|
158
167
|
reset
|
159
168
|
loaded!
|
160
169
|
end
|
161
170
|
end
|
162
171
|
|
163
|
-
# Called when the association is declared as :dependent => :delete_all. This is
|
164
|
-
# an optimised version which avoids loading the records into memory. Not really
|
165
|
-
# for public consumption.
|
166
|
-
def delete_all_on_destroy
|
167
|
-
scoped.delete_all
|
168
|
-
end
|
169
|
-
|
170
172
|
# Destroy all the records from this association.
|
171
173
|
#
|
172
174
|
# See destroy for more info.
|
@@ -177,21 +179,10 @@ module ActiveRecord
|
|
177
179
|
end
|
178
180
|
end
|
179
181
|
|
180
|
-
# Calculate sum using SQL, not Enumerable
|
181
|
-
def sum(*args)
|
182
|
-
if block_given?
|
183
|
-
scoped.sum(*args) { |*block_args| yield(*block_args) }
|
184
|
-
else
|
185
|
-
scoped.sum(*args)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
182
|
# Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
|
190
183
|
# association, it will be used for the query. Otherwise, construct options and pass them with
|
191
184
|
# scope to the target class's +count+.
|
192
185
|
def count(column_name = nil, count_options = {})
|
193
|
-
return 0 if owner.new_record?
|
194
|
-
|
195
186
|
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
196
187
|
|
197
188
|
if options[:counter_sql] || options[:finder_sql]
|
@@ -201,13 +192,14 @@ module ActiveRecord
|
|
201
192
|
|
202
193
|
reflection.klass.count_by_sql(custom_counter_sql)
|
203
194
|
else
|
204
|
-
|
195
|
+
relation = scope
|
196
|
+
if association_scope.distinct_value
|
205
197
|
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
206
198
|
column_name ||= reflection.klass.primary_key
|
207
|
-
|
199
|
+
relation = relation.distinct
|
208
200
|
end
|
209
201
|
|
210
|
-
value =
|
202
|
+
value = relation.count(column_name, count_options)
|
211
203
|
|
212
204
|
limit = options[:limit]
|
213
205
|
offset = options[:offset]
|
@@ -228,7 +220,27 @@ module ActiveRecord
|
|
228
220
|
# are actually removed from the database, that depends precisely on
|
229
221
|
# +delete_records+. They are in any case removed from the collection.
|
230
222
|
def delete(*records)
|
231
|
-
|
223
|
+
dependent = options[:dependent]
|
224
|
+
|
225
|
+
if records.first == :all
|
226
|
+
|
227
|
+
if dependent && dependent == :destroy
|
228
|
+
message = 'In Rails 4.1 delete_all on associations would not fire callbacks. ' \
|
229
|
+
'It means if the :dependent option is :destroy then the associated ' \
|
230
|
+
'records would be deleted without loading and invoking callbacks.'
|
231
|
+
|
232
|
+
ActiveRecord::Base.logger ? ActiveRecord::Base.logger.warn(message) : $stderr.puts(message)
|
233
|
+
end
|
234
|
+
|
235
|
+
if (loaded? || dependent == :destroy) && dependent != :delete_all
|
236
|
+
delete_or_destroy(load_target, dependent)
|
237
|
+
else
|
238
|
+
delete_records(:all, dependent)
|
239
|
+
end
|
240
|
+
else
|
241
|
+
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
242
|
+
delete_or_destroy(records, dependent)
|
243
|
+
end
|
232
244
|
end
|
233
245
|
|
234
246
|
# Destroy +records+ and remove them from this association calling
|
@@ -252,11 +264,15 @@ module ActiveRecord
|
|
252
264
|
# This method is abstract in the sense that it relies on
|
253
265
|
# +count_records+, which is a method descendants have to provide.
|
254
266
|
def size
|
255
|
-
if !find_target? ||
|
256
|
-
|
257
|
-
|
267
|
+
if !find_target? || loaded?
|
268
|
+
if association_scope.distinct_value
|
269
|
+
target.uniq.size
|
270
|
+
else
|
271
|
+
target.size
|
272
|
+
end
|
273
|
+
elsif !loaded? && !association_scope.group_values.empty?
|
258
274
|
load_target.size
|
259
|
-
elsif !loaded? && !
|
275
|
+
elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
|
260
276
|
unsaved_records = target.select { |r| r.new_record? }
|
261
277
|
unsaved_records.size + count_records
|
262
278
|
else
|
@@ -273,13 +289,24 @@ module ActiveRecord
|
|
273
289
|
load_target.size
|
274
290
|
end
|
275
291
|
|
276
|
-
#
|
277
|
-
#
|
278
|
-
#
|
292
|
+
# Returns true if the collection is empty.
|
293
|
+
#
|
294
|
+
# If the collection has been loaded or the <tt>:counter_sql</tt> option
|
295
|
+
# is provided, it is equivalent to <tt>collection.size.zero?</tt>. If the
|
296
|
+
# collection has not been loaded, it is equivalent to
|
297
|
+
# <tt>collection.exists?</tt>. If the collection has not already been
|
298
|
+
# loaded and you are going to fetch the records anyway it is better to
|
299
|
+
# check <tt>collection.length.zero?</tt>.
|
279
300
|
def empty?
|
280
|
-
|
301
|
+
if loaded? || options[:counter_sql]
|
302
|
+
size.zero?
|
303
|
+
else
|
304
|
+
@target.blank? && !scope.exists?
|
305
|
+
end
|
281
306
|
end
|
282
307
|
|
308
|
+
# Returns true if the collections is not empty.
|
309
|
+
# Equivalent to +!collection.empty?+.
|
283
310
|
def any?
|
284
311
|
if block_given?
|
285
312
|
load_target.any? { |*block_args| yield(*block_args) }
|
@@ -288,7 +315,8 @@ module ActiveRecord
|
|
288
315
|
end
|
289
316
|
end
|
290
317
|
|
291
|
-
# Returns true if the collection has more than 1 record.
|
318
|
+
# Returns true if the collection has more than 1 record.
|
319
|
+
# Equivalent to +collection.size > 1+.
|
292
320
|
def many?
|
293
321
|
if block_given?
|
294
322
|
load_target.many? { |*block_args| yield(*block_args) }
|
@@ -297,17 +325,18 @@ module ActiveRecord
|
|
297
325
|
end
|
298
326
|
end
|
299
327
|
|
300
|
-
def
|
328
|
+
def distinct
|
301
329
|
seen = {}
|
302
|
-
|
330
|
+
load_target.find_all do |record|
|
303
331
|
seen[record.id] = true unless seen.key?(record.id)
|
304
332
|
end
|
305
333
|
end
|
334
|
+
alias uniq distinct
|
306
335
|
|
307
|
-
# Replace this collection with +other_array
|
308
|
-
#
|
336
|
+
# Replace this collection with +other_array+. This will perform a diff
|
337
|
+
# and delete/add only records that have changed.
|
309
338
|
def replace(other_array)
|
310
|
-
other_array.each { |val| raise_on_type_mismatch(val) }
|
339
|
+
other_array.each { |val| raise_on_type_mismatch!(val) }
|
311
340
|
original_target = load_target.dup
|
312
341
|
|
313
342
|
if owner.new_record?
|
@@ -323,7 +352,7 @@ module ActiveRecord
|
|
323
352
|
include_in_memory?(record)
|
324
353
|
else
|
325
354
|
load_target if options[:finder_sql]
|
326
|
-
loaded? ? target.include?(record) :
|
355
|
+
loaded? ? target.include?(record) : scope.exists?(record)
|
327
356
|
end
|
328
357
|
else
|
329
358
|
false
|
@@ -343,7 +372,7 @@ module ActiveRecord
|
|
343
372
|
callback(:before_add, record)
|
344
373
|
yield(record) if block_given?
|
345
374
|
|
346
|
-
if
|
375
|
+
if association_scope.distinct_value && index = @target.index(record)
|
347
376
|
@target[index] = record
|
348
377
|
else
|
349
378
|
@target << record
|
@@ -355,6 +384,16 @@ module ActiveRecord
|
|
355
384
|
record
|
356
385
|
end
|
357
386
|
|
387
|
+
def scope(opts = {})
|
388
|
+
scope = super()
|
389
|
+
scope.none! if opts.fetch(:nullify, true) && null_scope?
|
390
|
+
scope
|
391
|
+
end
|
392
|
+
|
393
|
+
def null_scope?
|
394
|
+
owner.new_record? && !foreign_key_present?
|
395
|
+
end
|
396
|
+
|
358
397
|
private
|
359
398
|
|
360
399
|
def custom_counter_sql
|
@@ -379,10 +418,9 @@ module ActiveRecord
|
|
379
418
|
if options[:finder_sql]
|
380
419
|
reflection.klass.find_by_sql(custom_finder_sql)
|
381
420
|
else
|
382
|
-
|
421
|
+
scope.to_a
|
383
422
|
end
|
384
423
|
|
385
|
-
records = options[:uniq] ? uniq(records) : records
|
386
424
|
records.each { |record| set_inverse_instance(record) }
|
387
425
|
records
|
388
426
|
end
|
@@ -402,12 +440,7 @@ module ActiveRecord
|
|
402
440
|
return memory if persisted.empty?
|
403
441
|
|
404
442
|
persisted.map! do |record|
|
405
|
-
|
406
|
-
# record rather than memory.at(memory.index(record)). The behavior is fixed in 1.9.
|
407
|
-
mem_index = memory.index(record)
|
408
|
-
|
409
|
-
if mem_index
|
410
|
-
mem_record = memory.delete_at(mem_index)
|
443
|
+
if mem_record = memory.delete(record)
|
411
444
|
|
412
445
|
((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
|
413
446
|
mem_record[name] = record[name]
|
@@ -422,16 +455,16 @@ module ActiveRecord
|
|
422
455
|
persisted + memory
|
423
456
|
end
|
424
457
|
|
425
|
-
def
|
458
|
+
def _create_record(attributes, raise = false, &block)
|
426
459
|
unless owner.persisted?
|
427
460
|
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
428
461
|
end
|
429
462
|
|
430
463
|
if attributes.is_a?(Array)
|
431
|
-
attributes.collect { |attr|
|
464
|
+
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
432
465
|
else
|
433
466
|
transaction do
|
434
|
-
add_to_target(build_record(attributes
|
467
|
+
add_to_target(build_record(attributes)) do |record|
|
435
468
|
yield(record) if block_given?
|
436
469
|
insert_record(record, true, raise)
|
437
470
|
end
|
@@ -445,12 +478,12 @@ module ActiveRecord
|
|
445
478
|
end
|
446
479
|
|
447
480
|
def create_scope
|
448
|
-
|
481
|
+
scope.scope_for_create.stringify_keys
|
449
482
|
end
|
450
483
|
|
451
484
|
def delete_or_destroy(records, method)
|
452
485
|
records = records.flatten
|
453
|
-
records.each { |record| raise_on_type_mismatch(record) }
|
486
|
+
records.each { |record| raise_on_type_mismatch!(record) }
|
454
487
|
existing_records = records.reject { |r| r.new_record? }
|
455
488
|
|
456
489
|
if existing_records.empty?
|
@@ -491,9 +524,9 @@ module ActiveRecord
|
|
491
524
|
result = true
|
492
525
|
|
493
526
|
records.flatten.each do |record|
|
494
|
-
raise_on_type_mismatch(record)
|
495
|
-
add_to_target(record) do |
|
496
|
-
result &&= insert_record(
|
527
|
+
raise_on_type_mismatch!(record)
|
528
|
+
add_to_target(record) do |rec|
|
529
|
+
result &&= insert_record(rec) unless owner.new_record?
|
497
530
|
end
|
498
531
|
end
|
499
532
|
|
@@ -552,17 +585,18 @@ module ActiveRecord
|
|
552
585
|
end
|
553
586
|
end
|
554
587
|
|
555
|
-
# If using a custom finder_sql
|
588
|
+
# If using a custom finder_sql or if the :inverse_of option has been
|
589
|
+
# specified, then #find scans the entire collection.
|
556
590
|
def find_by_scan(*args)
|
557
591
|
expects_array = args.first.kind_of?(Array)
|
558
|
-
ids = args.flatten.compact.
|
592
|
+
ids = args.flatten.compact.map{ |arg| arg.to_s }.uniq
|
559
593
|
|
560
594
|
if ids.size == 1
|
561
595
|
id = ids.first
|
562
|
-
record = load_target.detect { |r| id == r.id }
|
596
|
+
record = load_target.detect { |r| id == r.id.to_s }
|
563
597
|
expects_array ? [ record ] : record
|
564
598
|
else
|
565
|
-
load_target.select { |r| ids.include?(r.id) }
|
599
|
+
load_target.select { |r| ids.include?(r.id.to_s) }
|
566
600
|
end
|
567
601
|
end
|
568
602
|
|
@@ -570,7 +604,7 @@ module ActiveRecord
|
|
570
604
|
def first_or_last(type, *args)
|
571
605
|
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
572
606
|
|
573
|
-
collection = fetch_first_or_last_using_find?(args) ?
|
607
|
+
collection = fetch_first_or_last_using_find?(args) ? scope : load_target
|
574
608
|
collection.send(type, *args).tap do |record|
|
575
609
|
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
576
610
|
end
|