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,12 +1,10 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
require 'active_support/core_ext/class/attribute'
|
3
1
|
|
4
2
|
module ActiveRecord
|
5
3
|
module ReadonlyAttributes
|
6
4
|
extend ActiveSupport::Concern
|
7
5
|
|
8
6
|
included do
|
9
|
-
class_attribute :_attr_readonly, :
|
7
|
+
class_attribute :_attr_readonly, instance_accessor: false
|
10
8
|
self._attr_readonly = []
|
11
9
|
end
|
12
10
|
|
@@ -22,5 +20,11 @@ module ActiveRecord
|
|
22
20
|
self._attr_readonly
|
23
21
|
end
|
24
22
|
end
|
23
|
+
|
24
|
+
def _attr_readonly
|
25
|
+
message = "Instance level _attr_readonly method is deprecated, please use class level method."
|
26
|
+
ActiveSupport::Deprecation.warn message
|
27
|
+
defined?(@_attr_readonly) ? @_attr_readonly : self.class._attr_readonly
|
28
|
+
end
|
25
29
|
end
|
26
30
|
end
|
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/class/attribute'
|
2
|
-
require 'active_support/core_ext/object/inclusion'
|
3
|
-
|
4
1
|
module ActiveRecord
|
5
2
|
# = Active Record Reflection
|
6
3
|
module Reflection # :nodoc:
|
@@ -20,13 +17,13 @@ module ActiveRecord
|
|
20
17
|
# MacroReflection class has info for AggregateReflection and AssociationReflection
|
21
18
|
# classes.
|
22
19
|
module ClassMethods
|
23
|
-
def create_reflection(macro, name, options, active_record)
|
20
|
+
def create_reflection(macro, name, scope, options, active_record)
|
24
21
|
case macro
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
|
23
|
+
klass = options[:through] ? ThroughReflection : AssociationReflection
|
24
|
+
reflection = klass.new(macro, name, scope, options, active_record)
|
25
|
+
when :composed_of
|
26
|
+
reflection = AggregateReflection.new(macro, name, scope, options, active_record)
|
30
27
|
end
|
31
28
|
|
32
29
|
self.reflections = self.reflections.merge(name => reflection)
|
@@ -43,7 +40,8 @@ module ActiveRecord
|
|
43
40
|
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
|
44
41
|
#
|
45
42
|
def reflect_on_aggregation(aggregation)
|
46
|
-
|
43
|
+
reflection = reflections[aggregation]
|
44
|
+
reflection if reflection.is_a?(AggregateReflection)
|
47
45
|
end
|
48
46
|
|
49
47
|
# Returns an array of AssociationReflection objects for all the
|
@@ -67,7 +65,8 @@ module ActiveRecord
|
|
67
65
|
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
68
66
|
#
|
69
67
|
def reflect_on_association(association)
|
70
|
-
|
68
|
+
reflection = reflections[association]
|
69
|
+
reflection if reflection.is_a?(AssociationReflection)
|
71
70
|
end
|
72
71
|
|
73
72
|
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
@@ -76,25 +75,31 @@ module ActiveRecord
|
|
76
75
|
end
|
77
76
|
end
|
78
77
|
|
79
|
-
|
80
|
-
# Abstract base class for AggregateReflection and AssociationReflection. Objects of
|
78
|
+
# Base class for AggregateReflection and AssociationReflection. Objects of
|
81
79
|
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
80
|
+
#
|
81
|
+
# MacroReflection
|
82
|
+
# AggregateReflection
|
83
|
+
# AssociationReflection
|
84
|
+
# ThroughReflection
|
82
85
|
class MacroReflection
|
83
86
|
# Returns the name of the macro.
|
84
87
|
#
|
85
|
-
# <tt>composed_of :balance, :
|
88
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
|
86
89
|
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
|
87
90
|
attr_reader :name
|
88
91
|
|
89
92
|
# Returns the macro type.
|
90
93
|
#
|
91
|
-
# <tt>composed_of :balance, :
|
94
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:composed_of</tt>
|
92
95
|
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
93
96
|
attr_reader :macro
|
94
97
|
|
98
|
+
attr_reader :scope
|
99
|
+
|
95
100
|
# Returns the hash of options used for the macro.
|
96
101
|
#
|
97
|
-
# <tt>composed_of :balance, :
|
102
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>{ class_name: "Money" }</tt>
|
98
103
|
# <tt>has_many :clients</tt> returns +{}+
|
99
104
|
attr_reader :options
|
100
105
|
|
@@ -102,9 +107,10 @@ module ActiveRecord
|
|
102
107
|
|
103
108
|
attr_reader :plural_name # :nodoc:
|
104
109
|
|
105
|
-
def initialize(macro, name, options, active_record)
|
110
|
+
def initialize(macro, name, scope, options, active_record)
|
106
111
|
@macro = macro
|
107
112
|
@name = name
|
113
|
+
@scope = scope
|
108
114
|
@options = options
|
109
115
|
@active_record = active_record
|
110
116
|
@plural_name = active_record.pluralize_table_names ?
|
@@ -113,7 +119,7 @@ module ActiveRecord
|
|
113
119
|
|
114
120
|
# Returns the class for the macro.
|
115
121
|
#
|
116
|
-
# <tt>composed_of :balance, :
|
122
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
|
117
123
|
# <tt>has_many :clients</tt> returns the Client class
|
118
124
|
def klass
|
119
125
|
@klass ||= class_name.constantize
|
@@ -121,7 +127,7 @@ module ActiveRecord
|
|
121
127
|
|
122
128
|
# Returns the class name for the macro.
|
123
129
|
#
|
124
|
-
# <tt>composed_of :balance, :
|
130
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
125
131
|
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
126
132
|
def class_name
|
127
133
|
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
@@ -137,10 +143,6 @@ module ActiveRecord
|
|
137
143
|
active_record == other_aggregation.active_record
|
138
144
|
end
|
139
145
|
|
140
|
-
def sanitized_conditions #:nodoc:
|
141
|
-
@sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
|
142
|
-
end
|
143
|
-
|
144
146
|
private
|
145
147
|
def derive_class_name
|
146
148
|
name.to_s.camelize
|
@@ -151,6 +153,10 @@ module ActiveRecord
|
|
151
153
|
# Holds all the meta-data about an aggregation as it was specified in the
|
152
154
|
# Active Record class.
|
153
155
|
class AggregateReflection < MacroReflection #:nodoc:
|
156
|
+
def mapping
|
157
|
+
mapping = options[:mapping] || [name, name]
|
158
|
+
mapping.first.is_a?(Array) ? mapping : [mapping]
|
159
|
+
end
|
154
160
|
end
|
155
161
|
|
156
162
|
# Holds all the meta-data about an association as it was specified in the
|
@@ -172,15 +178,15 @@ module ActiveRecord
|
|
172
178
|
@klass ||= active_record.send(:compute_type, class_name)
|
173
179
|
end
|
174
180
|
|
175
|
-
def initialize(
|
181
|
+
def initialize(*args)
|
176
182
|
super
|
177
|
-
@collection =
|
183
|
+
@collection = [:has_many, :has_and_belongs_to_many].include?(macro)
|
178
184
|
end
|
179
185
|
|
180
|
-
# Returns a new, unsaved instance of the associated class. +
|
186
|
+
# Returns a new, unsaved instance of the associated class. +attributes+ will
|
181
187
|
# be passed to the class's constructor.
|
182
|
-
def build_association(
|
183
|
-
klass.new(
|
188
|
+
def build_association(attributes, &block)
|
189
|
+
klass.new(attributes, &block)
|
184
190
|
end
|
185
191
|
|
186
192
|
def table_name
|
@@ -191,6 +197,10 @@ module ActiveRecord
|
|
191
197
|
@quoted_table_name ||= klass.quoted_table_name
|
192
198
|
end
|
193
199
|
|
200
|
+
def join_table
|
201
|
+
@join_table ||= options[:join_table] || derive_join_table
|
202
|
+
end
|
203
|
+
|
194
204
|
def foreign_key
|
195
205
|
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
196
206
|
end
|
@@ -228,8 +238,8 @@ module ActiveRecord
|
|
228
238
|
end
|
229
239
|
end
|
230
240
|
|
231
|
-
def columns(tbl_name
|
232
|
-
@columns ||= klass.connection.columns(tbl_name
|
241
|
+
def columns(tbl_name)
|
242
|
+
@columns ||= klass.connection.columns(tbl_name)
|
233
243
|
end
|
234
244
|
|
235
245
|
def reset_column_information
|
@@ -238,6 +248,10 @@ module ActiveRecord
|
|
238
248
|
|
239
249
|
def check_validity!
|
240
250
|
check_validity_of_inverse!
|
251
|
+
|
252
|
+
if has_and_belongs_to_many? && association_foreign_key == foreign_key
|
253
|
+
raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(self)
|
254
|
+
end
|
241
255
|
end
|
242
256
|
|
243
257
|
def check_validity_of_inverse!
|
@@ -266,11 +280,10 @@ module ActiveRecord
|
|
266
280
|
false
|
267
281
|
end
|
268
282
|
|
269
|
-
# An array of arrays of
|
270
|
-
# in the #chain.
|
271
|
-
|
272
|
-
|
273
|
-
[[options[:conditions]].compact]
|
283
|
+
# An array of arrays of scopes. Each item in the outside array corresponds to a reflection
|
284
|
+
# in the #chain.
|
285
|
+
def scope_chain
|
286
|
+
scope ? [[scope]] : [[]]
|
274
287
|
end
|
275
288
|
|
276
289
|
alias :source_macro :macro
|
@@ -306,10 +319,10 @@ module ActiveRecord
|
|
306
319
|
# the parent's validation.
|
307
320
|
#
|
308
321
|
# Unless you explicitly disable validation with
|
309
|
-
# <tt
|
322
|
+
# <tt>validate: false</tt>, validation will take place when:
|
310
323
|
#
|
311
|
-
# * you explicitly enable validation; <tt
|
312
|
-
# * you use autosave; <tt
|
324
|
+
# * you explicitly enable validation; <tt>validate: true</tt>
|
325
|
+
# * you use autosave; <tt>autosave: true</tt>
|
313
326
|
# * the association is a +has_many+ association
|
314
327
|
def validate?
|
315
328
|
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
|
@@ -320,6 +333,10 @@ module ActiveRecord
|
|
320
333
|
macro == :belongs_to
|
321
334
|
end
|
322
335
|
|
336
|
+
def has_and_belongs_to_many?
|
337
|
+
macro == :has_and_belongs_to_many
|
338
|
+
end
|
339
|
+
|
323
340
|
def association_class
|
324
341
|
case macro
|
325
342
|
when :belongs_to
|
@@ -345,11 +362,15 @@ module ActiveRecord
|
|
345
362
|
end
|
346
363
|
end
|
347
364
|
|
365
|
+
def polymorphic?
|
366
|
+
options.key? :polymorphic
|
367
|
+
end
|
368
|
+
|
348
369
|
private
|
349
370
|
def derive_class_name
|
350
|
-
class_name = name.to_s
|
371
|
+
class_name = name.to_s
|
351
372
|
class_name = class_name.singularize if collection?
|
352
|
-
class_name
|
373
|
+
class_name.camelize
|
353
374
|
end
|
354
375
|
|
355
376
|
def derive_foreign_key
|
@@ -362,6 +383,10 @@ module ActiveRecord
|
|
362
383
|
end
|
363
384
|
end
|
364
385
|
|
386
|
+
def derive_join_table
|
387
|
+
[active_record.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
|
388
|
+
end
|
389
|
+
|
365
390
|
def primary_key(klass)
|
366
391
|
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
367
392
|
end
|
@@ -378,9 +403,19 @@ module ActiveRecord
|
|
378
403
|
#
|
379
404
|
# class Post < ActiveRecord::Base
|
380
405
|
# has_many :taggings
|
381
|
-
# has_many :tags, :
|
406
|
+
# has_many :tags, through: :taggings
|
382
407
|
# end
|
383
408
|
#
|
409
|
+
# class Tagging < ActiveRecord::Base
|
410
|
+
# belongs_to :post
|
411
|
+
# belongs_to :tag
|
412
|
+
# end
|
413
|
+
#
|
414
|
+
# tags_reflection = Post.reflect_on_association(:tags)
|
415
|
+
#
|
416
|
+
# taggings_reflection = tags_reflection.source_reflection
|
417
|
+
# # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags">
|
418
|
+
#
|
384
419
|
def source_reflection
|
385
420
|
@source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
|
386
421
|
end
|
@@ -390,7 +425,7 @@ module ActiveRecord
|
|
390
425
|
#
|
391
426
|
# class Post < ActiveRecord::Base
|
392
427
|
# has_many :taggings
|
393
|
-
# has_many :tags, :
|
428
|
+
# has_many :tags, through: :taggings
|
394
429
|
# end
|
395
430
|
#
|
396
431
|
# tags_reflection = Post.reflect_on_association(:tags)
|
@@ -406,6 +441,17 @@ module ActiveRecord
|
|
406
441
|
# The chain is built by recursively calling #chain on the source reflection and the through
|
407
442
|
# reflection. The base case for the recursion is a normal association, which just returns
|
408
443
|
# [self] as its #chain.
|
444
|
+
#
|
445
|
+
# class Post < ActiveRecord::Base
|
446
|
+
# has_many :taggings
|
447
|
+
# has_many :tags, through: :taggings
|
448
|
+
# end
|
449
|
+
#
|
450
|
+
# tags_reflection = Post.reflect_on_association(:tags)
|
451
|
+
# tags_reflection.chain
|
452
|
+
# # => [<ActiveRecord::Reflection::ThroughReflection: @macro=:has_many, @name=:tags, @options={:through=>:taggings}, @active_record=Post>,
|
453
|
+
# <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @options={}, @active_record=Post>]
|
454
|
+
#
|
409
455
|
def chain
|
410
456
|
@chain ||= begin
|
411
457
|
chain = source_reflection.chain + through_reflection.chain
|
@@ -418,41 +464,37 @@ module ActiveRecord
|
|
418
464
|
#
|
419
465
|
# class Person
|
420
466
|
# has_many :articles
|
421
|
-
# has_many :comment_tags, :
|
467
|
+
# has_many :comment_tags, through: :articles
|
422
468
|
# end
|
423
469
|
#
|
424
470
|
# class Article
|
425
471
|
# has_many :comments
|
426
|
-
# has_many :comment_tags, :
|
472
|
+
# has_many :comment_tags, through: :comments, source: :tags
|
427
473
|
# end
|
428
474
|
#
|
429
475
|
# class Comment
|
430
476
|
# has_many :tags
|
431
477
|
# end
|
432
478
|
#
|
433
|
-
# There may be
|
479
|
+
# There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
|
434
480
|
# but only Comment.tags will be represented in the #chain. So this method creates an array
|
435
|
-
# of
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
@conditions ||= begin
|
440
|
-
conditions = source_reflection.conditions.map { |c| c.dup }
|
481
|
+
# of scopes corresponding to the chain.
|
482
|
+
def scope_chain
|
483
|
+
@scope_chain ||= begin
|
484
|
+
scope_chain = source_reflection.scope_chain.map(&:dup)
|
441
485
|
|
442
|
-
# Add to it the
|
443
|
-
|
486
|
+
# Add to it the scope from this reflection (if any)
|
487
|
+
scope_chain.first << scope if scope
|
444
488
|
|
445
|
-
|
489
|
+
through_scope_chain = through_reflection.scope_chain.map(&:dup)
|
446
490
|
|
447
491
|
if options[:source_type]
|
448
|
-
|
492
|
+
through_scope_chain.first <<
|
493
|
+
through_reflection.klass.where(foreign_type => options[:source_type])
|
449
494
|
end
|
450
495
|
|
451
496
|
# Recursively fill out the rest of the array from the through reflection
|
452
|
-
|
453
|
-
|
454
|
-
# And return
|
455
|
-
conditions
|
497
|
+
scope_chain + through_scope_chain
|
456
498
|
end
|
457
499
|
end
|
458
500
|
|
@@ -480,9 +522,16 @@ module ActiveRecord
|
|
480
522
|
source_reflection.options[:primary_key] || primary_key(klass || self.klass)
|
481
523
|
end
|
482
524
|
|
483
|
-
# Gets an array of possible <tt>:through</tt> source reflection names
|
525
|
+
# Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
|
526
|
+
#
|
527
|
+
# class Post < ActiveRecord::Base
|
528
|
+
# has_many :taggings
|
529
|
+
# has_many :tags, through: :taggings
|
530
|
+
# end
|
484
531
|
#
|
485
|
-
#
|
532
|
+
# tags_reflection = Post.reflect_on_association(:tags)
|
533
|
+
# tags_reflection.source_reflection_names
|
534
|
+
# # => [:tag, :tags]
|
486
535
|
#
|
487
536
|
def source_reflection_names
|
488
537
|
@source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
|
@@ -1,21 +1,26 @@
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
2
1
|
|
3
2
|
module ActiveRecord
|
4
3
|
module Batches
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
4
|
+
# Looping through a collection of records from the database
|
5
|
+
# (using the +all+ method, for example) is very inefficient
|
6
|
+
# since it will try to instantiate all the objects at once.
|
8
7
|
#
|
9
|
-
#
|
8
|
+
# In that case, batch processing methods allow you to work
|
9
|
+
# with the records in batches, thereby greatly reducing memory consumption.
|
10
|
+
#
|
11
|
+
# The #find_each method uses #find_in_batches with a batch size of 1000 (or as
|
12
|
+
# specified by the +:batch_size+ option).
|
13
|
+
#
|
14
|
+
# Person.all.find_each do |person|
|
15
|
+
# person.do_awesome_stuff
|
16
|
+
# end
|
10
17
|
#
|
11
18
|
# Person.where("age > 21").find_each do |person|
|
12
19
|
# person.party_all_night!
|
13
20
|
# end
|
14
21
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# you just need to loop over less than 1000 records, it's probably
|
18
|
-
# better just to use the regular find methods.
|
22
|
+
# You can also pass the +:start+ option to specify
|
23
|
+
# an offset to control the starting point.
|
19
24
|
def find_each(options = {})
|
20
25
|
find_in_batches(options) do |records|
|
21
26
|
records.each { |record| yield record }
|
@@ -23,40 +28,38 @@ module ActiveRecord
|
|
23
28
|
end
|
24
29
|
|
25
30
|
# Yields each batch of records that was found by the find +options+ as
|
26
|
-
# an array. The size of each batch is set by the
|
31
|
+
# an array. The size of each batch is set by the +:batch_size+
|
27
32
|
# option; the default is 1000.
|
28
33
|
#
|
29
34
|
# You can control the starting point for the batch processing by
|
30
|
-
# supplying the
|
35
|
+
# supplying the +:start+ option. This is especially useful if you
|
31
36
|
# want multiple workers dealing with the same processing queue. You can
|
32
37
|
# make worker 1 handle all the records between id 0 and 10,000 and
|
33
|
-
# worker 2 handle from 10,000 and beyond (by setting the
|
38
|
+
# worker 2 handle from 10,000 and beyond (by setting the +:start+
|
34
39
|
# option on that worker).
|
35
40
|
#
|
36
41
|
# It's not possible to set the order. That is automatically set to
|
37
42
|
# ascending on the primary key ("id ASC") to make the batch ordering
|
38
|
-
# work. This also
|
43
|
+
# work. This also means that this method only works with integer-based
|
39
44
|
# primary keys. You can't set the limit either, that's used to control
|
40
45
|
# the batch sizes.
|
41
46
|
#
|
42
|
-
# Example:
|
43
|
-
#
|
44
47
|
# Person.where("age > 21").find_in_batches do |group|
|
45
48
|
# sleep(50) # Make sure it doesn't get too crowded in there!
|
46
49
|
# group.each { |person| person.party_all_night! }
|
47
50
|
# end
|
51
|
+
#
|
52
|
+
# # Let's process the next 2000 records
|
53
|
+
# Person.all.find_in_batches(start: 2000, batch_size: 2000) do |group|
|
54
|
+
# group.each { |person| person.party_all_night! }
|
55
|
+
# end
|
48
56
|
def find_in_batches(options = {})
|
49
|
-
|
57
|
+
options.assert_valid_keys(:start, :batch_size)
|
50
58
|
|
51
|
-
|
52
|
-
ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
|
53
|
-
end
|
54
|
-
|
55
|
-
if (finder_options = options.except(:start, :batch_size)).present?
|
56
|
-
raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
|
57
|
-
raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit].present?
|
59
|
+
relation = self
|
58
60
|
|
59
|
-
|
61
|
+
if logger && (arel.orders.present? || arel.taken.present?)
|
62
|
+
logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
|
60
63
|
end
|
61
64
|
|
62
65
|
start = options.delete(:start)
|
@@ -68,16 +71,13 @@ module ActiveRecord
|
|
68
71
|
while records.any?
|
69
72
|
records_size = records.size
|
70
73
|
primary_key_offset = records.last.id
|
74
|
+
raise "Primary key not included in the custom select clause" unless primary_key_offset
|
71
75
|
|
72
76
|
yield records
|
73
77
|
|
74
78
|
break if records_size < batch_size
|
75
79
|
|
76
|
-
|
77
|
-
records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
|
78
|
-
else
|
79
|
-
raise "Primary key not included in the custom select clause"
|
80
|
-
end
|
80
|
+
records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|