activerecord 4.1.16 → 4.2.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1162 -1801
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- 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 +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -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/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +56 -94
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- 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 +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -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 +15 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -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 +19 -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 +109 -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/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +71 -46
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- 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 +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -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 +23 -0
- data/lib/active_record/type/integer.rb +59 -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 +62 -0
- data/lib/active_record/type/string.rb +40 -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 +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +4 -0
- 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 +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
data/lib/active_record/core.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
require 'thread'
|
1
2
|
require 'active_support/core_ext/hash/indifferent_access'
|
2
3
|
require 'active_support/core_ext/object/duplicable'
|
3
|
-
require '
|
4
|
+
require 'active_support/core_ext/string/filters'
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
module Core
|
@@ -16,7 +17,6 @@ module ActiveRecord
|
|
16
17
|
mattr_accessor :logger, instance_writer: false
|
17
18
|
|
18
19
|
##
|
19
|
-
# :singleton-method:
|
20
20
|
# Contains the database configuration - as is typically stored in config/database.yml -
|
21
21
|
# as a Hash.
|
22
22
|
#
|
@@ -88,11 +88,14 @@ module ActiveRecord
|
|
88
88
|
mattr_accessor :maintain_test_schema, instance_accessor: false
|
89
89
|
|
90
90
|
def self.disable_implicit_join_references=(value)
|
91
|
-
ActiveSupport::Deprecation.warn(
|
92
|
-
|
91
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
92
|
+
Implicit join references were removed with Rails 4.1.
|
93
|
+
Make sure to remove this configuration because it does nothing.
|
94
|
+
MSG
|
93
95
|
end
|
94
96
|
|
95
97
|
class_attribute :default_connection_handler, instance_writer: false
|
98
|
+
class_attribute :find_by_statement_cache
|
96
99
|
|
97
100
|
def self.connection_handler
|
98
101
|
ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
|
@@ -106,7 +109,94 @@ module ActiveRecord
|
|
106
109
|
end
|
107
110
|
|
108
111
|
module ClassMethods
|
109
|
-
def
|
112
|
+
def allocate
|
113
|
+
define_attribute_methods
|
114
|
+
super
|
115
|
+
end
|
116
|
+
|
117
|
+
def initialize_find_by_cache # :nodoc:
|
118
|
+
self.find_by_statement_cache = {}.extend(Mutex_m)
|
119
|
+
end
|
120
|
+
|
121
|
+
def inherited(child_class) # :nodoc:
|
122
|
+
child_class.initialize_find_by_cache
|
123
|
+
super
|
124
|
+
end
|
125
|
+
|
126
|
+
def find(*ids) # :nodoc:
|
127
|
+
# We don't have cache keys for this stuff yet
|
128
|
+
return super unless ids.length == 1
|
129
|
+
# Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
|
130
|
+
return super if ids.first.kind_of?(Symbol)
|
131
|
+
return super if block_given? ||
|
132
|
+
primary_key.nil? ||
|
133
|
+
default_scopes.any? ||
|
134
|
+
current_scope ||
|
135
|
+
columns_hash.include?(inheritance_column) ||
|
136
|
+
ids.first.kind_of?(Array)
|
137
|
+
|
138
|
+
id = ids.first
|
139
|
+
if ActiveRecord::Base === id
|
140
|
+
id = id.id
|
141
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
142
|
+
You are passing an instance of ActiveRecord::Base to `find`.
|
143
|
+
Please pass the id of the object by calling `.id`
|
144
|
+
MSG
|
145
|
+
end
|
146
|
+
key = primary_key
|
147
|
+
|
148
|
+
s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
|
149
|
+
find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
|
150
|
+
where(key => params.bind).limit(1)
|
151
|
+
}
|
152
|
+
}
|
153
|
+
record = s.execute([id], self, connection).first
|
154
|
+
unless record
|
155
|
+
raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
|
156
|
+
end
|
157
|
+
record
|
158
|
+
rescue RangeError
|
159
|
+
raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
|
160
|
+
end
|
161
|
+
|
162
|
+
def find_by(*args) # :nodoc:
|
163
|
+
return super if current_scope || !(Hash === args.first) || reflect_on_all_aggregations.any?
|
164
|
+
return super if default_scopes.any?
|
165
|
+
|
166
|
+
hash = args.first
|
167
|
+
|
168
|
+
return super if hash.values.any? { |v|
|
169
|
+
v.nil? || Array === v || Hash === v
|
170
|
+
}
|
171
|
+
|
172
|
+
# We can't cache Post.find_by(author: david) ...yet
|
173
|
+
return super unless hash.keys.all? { |k| columns_hash.has_key?(k.to_s) }
|
174
|
+
|
175
|
+
key = hash.keys
|
176
|
+
|
177
|
+
klass = self
|
178
|
+
s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
|
179
|
+
find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
|
180
|
+
wheres = key.each_with_object({}) { |param,o|
|
181
|
+
o[param] = params.bind
|
182
|
+
}
|
183
|
+
klass.where(wheres).limit(1)
|
184
|
+
}
|
185
|
+
}
|
186
|
+
begin
|
187
|
+
s.execute(hash.values, self, connection).first
|
188
|
+
rescue TypeError => e
|
189
|
+
raise ActiveRecord::StatementInvalid.new(e.message, e)
|
190
|
+
rescue RangeError
|
191
|
+
nil
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def find_by!(*args) # :nodoc:
|
196
|
+
find_by(*args) or raise RecordNotFound.new("Couldn't find #{name}")
|
197
|
+
end
|
198
|
+
|
199
|
+
def initialize_generated_modules # :nodoc:
|
110
200
|
generated_association_methods
|
111
201
|
end
|
112
202
|
|
@@ -180,12 +270,8 @@ module ActiveRecord
|
|
180
270
|
# # Instantiates a single new object
|
181
271
|
# User.new(first_name: 'Jamie')
|
182
272
|
def initialize(attributes = nil, options = {})
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
@attributes = self.class.initialize_attributes(defaults)
|
187
|
-
@column_types_override = nil
|
188
|
-
@column_types = self.class.column_types
|
273
|
+
@attributes = self.class._default_attributes.dup
|
274
|
+
self.class.define_attribute_methods
|
189
275
|
|
190
276
|
init_internals
|
191
277
|
initialize_internals_callback
|
@@ -195,32 +281,35 @@ module ActiveRecord
|
|
195
281
|
init_attributes(attributes, options) if attributes
|
196
282
|
|
197
283
|
yield self if block_given?
|
198
|
-
|
284
|
+
_run_initialize_callbacks
|
199
285
|
end
|
200
286
|
|
201
|
-
# Initialize an empty model object from +coder+. +coder+
|
202
|
-
# the
|
203
|
-
#
|
287
|
+
# Initialize an empty model object from +coder+. +coder+ should be
|
288
|
+
# the result of previously encoding an Active Record model, using
|
289
|
+
# `encode_with`
|
204
290
|
#
|
205
291
|
# class Post < ActiveRecord::Base
|
206
292
|
# end
|
207
293
|
#
|
294
|
+
# old_post = Post.new(title: "hello world")
|
295
|
+
# coder = {}
|
296
|
+
# old_post.encode_with(coder)
|
297
|
+
#
|
208
298
|
# post = Post.allocate
|
209
|
-
# post.init_with(
|
299
|
+
# post.init_with(coder)
|
210
300
|
# post.title # => 'hello world'
|
211
301
|
def init_with(coder)
|
212
|
-
|
213
|
-
@
|
214
|
-
@column_types = self.class.column_types
|
302
|
+
coder = LegacyYamlAdapter.convert(self.class, coder)
|
303
|
+
@attributes = coder['attributes']
|
215
304
|
|
216
305
|
init_internals
|
217
306
|
|
218
|
-
@new_record =
|
307
|
+
@new_record = coder['new_record']
|
219
308
|
|
220
309
|
self.class.define_attribute_methods
|
221
310
|
|
222
|
-
|
223
|
-
|
311
|
+
_run_find_callbacks
|
312
|
+
_run_initialize_callbacks
|
224
313
|
|
225
314
|
self
|
226
315
|
end
|
@@ -253,17 +342,13 @@ module ActiveRecord
|
|
253
342
|
|
254
343
|
##
|
255
344
|
def initialize_dup(other) # :nodoc:
|
256
|
-
|
257
|
-
self.class.
|
258
|
-
|
259
|
-
@attributes = cloned_attributes
|
260
|
-
@attributes[self.class.primary_key] = nil
|
345
|
+
@attributes = @attributes.dup
|
346
|
+
@attributes.reset(self.class.primary_key)
|
261
347
|
|
262
|
-
|
348
|
+
_run_initialize_callbacks
|
263
349
|
|
264
350
|
@aggregation_cache = {}
|
265
351
|
@association_cache = {}
|
266
|
-
@attributes_cache = {}
|
267
352
|
|
268
353
|
@new_record = true
|
269
354
|
@destroyed = false
|
@@ -284,7 +369,11 @@ module ActiveRecord
|
|
284
369
|
# Post.new.encode_with(coder)
|
285
370
|
# coder # => {"attributes" => {"id" => nil, ... }}
|
286
371
|
def encode_with(coder)
|
287
|
-
|
372
|
+
# FIXME: Remove this when we better serialize attributes
|
373
|
+
coder['raw_attributes'] = attributes_before_type_cast
|
374
|
+
coder['attributes'] = @attributes
|
375
|
+
coder['new_record'] = new_record?
|
376
|
+
coder['active_record_yaml_version'] = 0
|
288
377
|
end
|
289
378
|
|
290
379
|
# Returns true if +comparison_object+ is the same exact object, or +comparison_object+
|
@@ -307,7 +396,11 @@ module ActiveRecord
|
|
307
396
|
# Delegates to id in order to allow two records of the same type and id to work with something like:
|
308
397
|
# [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
|
309
398
|
def hash
|
310
|
-
id
|
399
|
+
if id
|
400
|
+
id.hash
|
401
|
+
else
|
402
|
+
super
|
403
|
+
end
|
311
404
|
end
|
312
405
|
|
313
406
|
# Clone and freeze the attributes hash such that associations are still
|
@@ -363,21 +456,45 @@ module ActiveRecord
|
|
363
456
|
"#<#{self.class} #{inspection}>"
|
364
457
|
end
|
365
458
|
|
459
|
+
# Takes a PP and prettily prints this record to it, allowing you to get a nice result from `pp record`
|
460
|
+
# when pp is required.
|
461
|
+
def pretty_print(pp)
|
462
|
+
return super if custom_inspect_method_defined?
|
463
|
+
pp.object_address_group(self) do
|
464
|
+
if defined?(@attributes) && @attributes
|
465
|
+
column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
|
466
|
+
pp.seplist(column_names, proc { pp.text ',' }) do |column_name|
|
467
|
+
column_value = read_attribute(column_name)
|
468
|
+
pp.breakable ' '
|
469
|
+
pp.group(1) do
|
470
|
+
pp.text column_name
|
471
|
+
pp.text ':'
|
472
|
+
pp.breakable
|
473
|
+
pp.pp column_value
|
474
|
+
end
|
475
|
+
end
|
476
|
+
else
|
477
|
+
pp.breakable ' '
|
478
|
+
pp.text 'not initialized'
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
366
483
|
# Returns a hash of the given methods with their names as keys and returned values as values.
|
367
484
|
def slice(*methods)
|
368
485
|
Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
|
369
486
|
end
|
370
487
|
|
488
|
+
private
|
489
|
+
|
371
490
|
def set_transaction_state(state) # :nodoc:
|
372
491
|
@transaction_state = state
|
373
492
|
end
|
374
493
|
|
375
494
|
def has_transactional_callbacks? # :nodoc:
|
376
|
-
!_rollback_callbacks.empty? || !_commit_callbacks.empty?
|
495
|
+
!_rollback_callbacks.empty? || !_commit_callbacks.empty?
|
377
496
|
end
|
378
497
|
|
379
|
-
private
|
380
|
-
|
381
498
|
# Updates the attributes on this particular ActiveRecord object so that
|
382
499
|
# if it is associated with a transaction, then the state of the AR object
|
383
500
|
# will be updated to reflect the current state of the transaction
|
@@ -400,6 +517,8 @@ module ActiveRecord
|
|
400
517
|
end
|
401
518
|
|
402
519
|
def update_attributes_from_transaction_state(transaction_state, depth)
|
520
|
+
@reflects_state = [false] if depth == 0
|
521
|
+
|
403
522
|
if transaction_state && transaction_state.finalized? && !has_transactional_callbacks?
|
404
523
|
unless @reflects_state[depth]
|
405
524
|
restore_transaction_record_state if transaction_state.rolledback?
|
@@ -426,12 +545,8 @@ module ActiveRecord
|
|
426
545
|
end
|
427
546
|
|
428
547
|
def init_internals
|
429
|
-
pk = self.class.primary_key
|
430
|
-
@attributes[pk] = nil unless @attributes.key?(pk)
|
431
|
-
|
432
548
|
@aggregation_cache = {}
|
433
549
|
@association_cache = {}
|
434
|
-
@attributes_cache = {}
|
435
550
|
@readonly = false
|
436
551
|
@destroyed = false
|
437
552
|
@marked_for_destruction = false
|
@@ -440,7 +555,6 @@ module ActiveRecord
|
|
440
555
|
@txn = nil
|
441
556
|
@_start_transaction_state = {}
|
442
557
|
@transaction_state = nil
|
443
|
-
@reflects_state = [false]
|
444
558
|
end
|
445
559
|
|
446
560
|
def initialize_internals_callback
|
@@ -451,5 +565,15 @@ module ActiveRecord
|
|
451
565
|
def init_attributes(attributes, options)
|
452
566
|
assign_attributes(attributes)
|
453
567
|
end
|
568
|
+
|
569
|
+
def thaw
|
570
|
+
if frozen?
|
571
|
+
@attributes = @attributes.dup
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
def custom_inspect_method_defined?
|
576
|
+
self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
|
577
|
+
end
|
454
578
|
end
|
455
579
|
end
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
# ==== Parameters
|
12
12
|
#
|
13
13
|
# * +id+ - The id of the object you wish to reset a counter on.
|
14
|
-
# * +counters+ - One or more association counters to reset
|
14
|
+
# * +counters+ - One or more association counters to reset. Association name or counter name can be given.
|
15
15
|
#
|
16
16
|
# ==== Examples
|
17
17
|
#
|
@@ -19,9 +19,14 @@ module ActiveRecord
|
|
19
19
|
# Post.reset_counters(1, :comments)
|
20
20
|
def reset_counters(id, *counters)
|
21
21
|
object = find(id)
|
22
|
-
counters.each do |
|
23
|
-
has_many_association = _reflect_on_association(
|
24
|
-
|
22
|
+
counters.each do |counter_association|
|
23
|
+
has_many_association = _reflect_on_association(counter_association)
|
24
|
+
unless has_many_association
|
25
|
+
has_many = reflect_on_all_associations(:has_many)
|
26
|
+
has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
|
27
|
+
counter_association = has_many_association.plural_name if has_many_association
|
28
|
+
end
|
29
|
+
raise ArgumentError, "'#{self.name}' has no association called '#{counter_association}'" unless has_many_association
|
25
30
|
|
26
31
|
if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
|
27
32
|
has_many_association = has_many_association.through_reflection
|
@@ -29,11 +34,11 @@ module ActiveRecord
|
|
29
34
|
|
30
35
|
foreign_key = has_many_association.foreign_key.to_s
|
31
36
|
child_class = has_many_association.klass
|
32
|
-
reflection = child_class._reflections.values.find { |e|
|
37
|
+
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
33
38
|
counter_name = reflection.counter_cache_column
|
34
39
|
|
35
40
|
stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
|
36
|
-
arel_table[counter_name] => object.send(
|
41
|
+
arel_table[counter_name] => object.send(counter_association).count(:all)
|
37
42
|
}, primary_key)
|
38
43
|
connection.update stmt
|
39
44
|
end
|
@@ -117,5 +122,54 @@ module ActiveRecord
|
|
117
122
|
update_counters(id, counter_name => -1)
|
118
123
|
end
|
119
124
|
end
|
125
|
+
|
126
|
+
protected
|
127
|
+
|
128
|
+
def actually_destroyed?
|
129
|
+
@_actually_destroyed
|
130
|
+
end
|
131
|
+
|
132
|
+
def clear_destroy_state
|
133
|
+
@_actually_destroyed = nil
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def _create_record(*)
|
139
|
+
id = super
|
140
|
+
|
141
|
+
each_counter_cached_associations do |association|
|
142
|
+
if send(association.reflection.name)
|
143
|
+
association.increment_counters
|
144
|
+
@_after_create_counter_called = true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
id
|
149
|
+
end
|
150
|
+
|
151
|
+
def destroy_row
|
152
|
+
affected_rows = super
|
153
|
+
|
154
|
+
if affected_rows > 0
|
155
|
+
each_counter_cached_associations do |association|
|
156
|
+
foreign_key = association.reflection.foreign_key.to_sym
|
157
|
+
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
|
158
|
+
if send(association.reflection.name)
|
159
|
+
association.decrement_counters
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
affected_rows
|
166
|
+
end
|
167
|
+
|
168
|
+
def each_counter_cached_associations
|
169
|
+
_reflections.each do |name, reflection|
|
170
|
+
yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
120
174
|
end
|
121
175
|
end
|
data/lib/active_record/enum.rb
CHANGED
@@ -18,17 +18,18 @@ module ActiveRecord
|
|
18
18
|
# conversation.archived? # => true
|
19
19
|
# conversation.status # => "archived"
|
20
20
|
#
|
21
|
-
# # conversation.
|
21
|
+
# # conversation.status = 1
|
22
22
|
# conversation.status = "archived"
|
23
23
|
#
|
24
|
-
# # conversation.update! status: nil
|
25
24
|
# conversation.status = nil
|
26
25
|
# conversation.status.nil? # => true
|
27
26
|
# conversation.status # => nil
|
28
27
|
#
|
29
28
|
# Scopes based on the allowed values of the enum field will be provided
|
30
|
-
# as well. With the above example
|
31
|
-
#
|
29
|
+
# as well. With the above example:
|
30
|
+
#
|
31
|
+
# Conversation.active
|
32
|
+
# Conversation.archived
|
32
33
|
#
|
33
34
|
# You can set the default value from the database declaration, like:
|
34
35
|
#
|
@@ -138,19 +139,16 @@ module ActiveRecord
|
|
138
139
|
@_enum_methods_module ||= begin
|
139
140
|
mod = Module.new do
|
140
141
|
private
|
141
|
-
def save_changed_attribute(attr_name,
|
142
|
+
def save_changed_attribute(attr_name, old)
|
142
143
|
if (mapping = self.class.defined_enums[attr_name.to_s])
|
144
|
+
value = _read_attribute(attr_name)
|
143
145
|
if attribute_changed?(attr_name)
|
144
|
-
old = changed_attributes[attr_name]
|
145
|
-
|
146
146
|
if mapping[old] == value
|
147
|
-
|
147
|
+
clear_attribute_changes([attr_name])
|
148
148
|
end
|
149
149
|
else
|
150
|
-
old = clone_attribute_value(:read_attribute, attr_name)
|
151
|
-
|
152
150
|
if old != value
|
153
|
-
|
151
|
+
set_attribute_was(attr_name, mapping.key(old))
|
154
152
|
end
|
155
153
|
end
|
156
154
|
else
|
data/lib/active_record/errors.rb
CHANGED
@@ -30,17 +30,18 @@ module ActiveRecord
|
|
30
30
|
class SerializationTypeMismatch < ActiveRecordError
|
31
31
|
end
|
32
32
|
|
33
|
-
# Raised when adapter not specified on connection (or configuration file
|
34
|
-
# misses adapter field).
|
33
|
+
# Raised when adapter not specified on connection (or configuration file
|
34
|
+
# +config/database.yml+ misses adapter field).
|
35
35
|
class AdapterNotSpecified < ActiveRecordError
|
36
36
|
end
|
37
37
|
|
38
|
-
# Raised when Active Record cannot find database adapter specified in
|
38
|
+
# Raised when Active Record cannot find database adapter specified in
|
39
|
+
# +config/database.yml+ or programmatically.
|
39
40
|
class AdapterNotFound < ActiveRecordError
|
40
41
|
end
|
41
42
|
|
42
|
-
# Raised when connection to the database could not been established (for
|
43
|
-
# is given a nil object).
|
43
|
+
# Raised when connection to the database could not been established (for
|
44
|
+
# example when +connection=+ is given a nil object).
|
44
45
|
class ConnectionNotEstablished < ActiveRecordError
|
45
46
|
end
|
46
47
|
|
@@ -51,10 +52,29 @@ module ActiveRecord
|
|
51
52
|
# Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
|
52
53
|
# saved because record is invalid.
|
53
54
|
class RecordNotSaved < ActiveRecordError
|
55
|
+
attr_reader :record
|
56
|
+
|
57
|
+
def initialize(message, record = nil)
|
58
|
+
@record = record
|
59
|
+
super(message)
|
60
|
+
end
|
54
61
|
end
|
55
62
|
|
56
63
|
# Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
|
64
|
+
#
|
65
|
+
# begin
|
66
|
+
# complex_operation_that_internally_calls_destroy!
|
67
|
+
# rescue ActiveRecord::RecordNotDestroyed => invalid
|
68
|
+
# puts invalid.record.errors
|
69
|
+
# end
|
70
|
+
#
|
57
71
|
class RecordNotDestroyed < ActiveRecordError
|
72
|
+
attr_reader :record
|
73
|
+
|
74
|
+
def initialize(message, record = nil)
|
75
|
+
@record = record
|
76
|
+
super(message)
|
77
|
+
end
|
58
78
|
end
|
59
79
|
|
60
80
|
# Superclass for all database execution errors.
|
@@ -82,35 +102,26 @@ module ActiveRecord
|
|
82
102
|
class InvalidForeignKey < WrappedDatabaseException
|
83
103
|
end
|
84
104
|
|
85
|
-
# Raised when number of bind variables in statement given to
|
86
|
-
# when using +find+ method)
|
87
|
-
#
|
105
|
+
# Raised when number of bind variables in statement given to +:condition+ key
|
106
|
+
# (for example, when using +find+ method) does not match number of expected
|
107
|
+
# values supplied.
|
88
108
|
#
|
89
|
-
# For example,
|
109
|
+
# For example, when there are two placeholders with only one value supplied:
|
90
110
|
#
|
91
111
|
# Location.where("lat = ? AND lng = ?", 53.7362)
|
92
|
-
#
|
93
|
-
# two placeholders are given but only one variable to fill them.
|
94
112
|
class PreparedStatementInvalid < ActiveRecordError
|
95
113
|
end
|
96
114
|
|
97
|
-
# Raised when a given database does not exist
|
98
|
-
class NoDatabaseError <
|
99
|
-
def initialize(message)
|
100
|
-
super extend_message(message)
|
101
|
-
end
|
102
|
-
|
103
|
-
# can be over written to add additional error information.
|
104
|
-
def extend_message(message)
|
105
|
-
message
|
106
|
-
end
|
115
|
+
# Raised when a given database does not exist.
|
116
|
+
class NoDatabaseError < StatementInvalid
|
107
117
|
end
|
108
118
|
|
109
119
|
# Raised on attempt to save stale record. Record is stale when it's being saved in another query after
|
110
120
|
# instantiation, for example, when two users edit the same wiki page and one starts editing and saves
|
111
121
|
# the page before the other.
|
112
122
|
#
|
113
|
-
# Read more about optimistic locking in ActiveRecord::Locking module
|
123
|
+
# Read more about optimistic locking in ActiveRecord::Locking module
|
124
|
+
# documentation.
|
114
125
|
class StaleObjectError < ActiveRecordError
|
115
126
|
attr_reader :record, :attempted_action
|
116
127
|
|
@@ -122,8 +133,9 @@ module ActiveRecord
|
|
122
133
|
|
123
134
|
end
|
124
135
|
|
125
|
-
# Raised when association is being configured improperly or
|
126
|
-
#
|
136
|
+
# Raised when association is being configured improperly or user tries to use
|
137
|
+
# offset and limit together with +has_many+ or +has_and_belongs_to_many+
|
138
|
+
# associations.
|
127
139
|
class ConfigurationError < ActiveRecordError
|
128
140
|
end
|
129
141
|
|
@@ -161,7 +173,8 @@ module ActiveRecord
|
|
161
173
|
class Rollback < ActiveRecordError
|
162
174
|
end
|
163
175
|
|
164
|
-
# Raised when attribute has a name reserved by Active Record (when attribute
|
176
|
+
# Raised when attribute has a name reserved by Active Record (when attribute
|
177
|
+
# has name of one of Active Record instance methods).
|
165
178
|
class DangerousAttributeError < ActiveRecordError
|
166
179
|
end
|
167
180
|
|
@@ -173,16 +186,17 @@ module ActiveRecord
|
|
173
186
|
def initialize(record, attribute)
|
174
187
|
@record = record
|
175
188
|
@attribute = attribute.to_s
|
176
|
-
super("unknown attribute
|
189
|
+
super("unknown attribute '#{attribute}' for #{@record.class}.")
|
177
190
|
end
|
178
191
|
|
179
192
|
end
|
180
193
|
|
181
194
|
# Raised when an error occurred while doing a mass assignment to an attribute through the
|
182
|
-
#
|
195
|
+
# +attributes=+ method. The exception has an +attribute+ property that is the name of the
|
183
196
|
# offending attribute.
|
184
197
|
class AttributeAssignmentError < ActiveRecordError
|
185
198
|
attr_reader :exception, :attribute
|
199
|
+
|
186
200
|
def initialize(message, exception, attribute)
|
187
201
|
super(message)
|
188
202
|
@exception = exception
|
@@ -195,6 +209,7 @@ module ActiveRecord
|
|
195
209
|
# objects, each corresponding to the error while assigning to an attribute.
|
196
210
|
class MultiparameterAssignmentErrors < ActiveRecordError
|
197
211
|
attr_reader :errors
|
212
|
+
|
198
213
|
def initialize(errors)
|
199
214
|
@errors = errors
|
200
215
|
end
|
@@ -204,11 +219,12 @@ module ActiveRecord
|
|
204
219
|
class UnknownPrimaryKey < ActiveRecordError
|
205
220
|
attr_reader :model
|
206
221
|
|
207
|
-
def initialize(model)
|
208
|
-
|
222
|
+
def initialize(model, description = nil)
|
223
|
+
message = "Unknown primary key for table #{model.table_name} in model #{model}."
|
224
|
+
message += "\n#{description}" if description
|
225
|
+
super(message)
|
209
226
|
@model = model
|
210
227
|
end
|
211
|
-
|
212
228
|
end
|
213
229
|
|
214
230
|
# Raised when a relation cannot be mutated because it's already loaded.
|
@@ -225,6 +241,13 @@ module ActiveRecord
|
|
225
241
|
class ImmutableRelation < ActiveRecordError
|
226
242
|
end
|
227
243
|
|
244
|
+
# TransactionIsolationError will be raised under the following conditions:
|
245
|
+
#
|
246
|
+
# * The adapter does not support setting the isolation level
|
247
|
+
# * You are joining an existing open transaction
|
248
|
+
# * You are creating a nested (savepoint) transaction
|
249
|
+
#
|
250
|
+
# The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
|
228
251
|
class TransactionIsolationError < ActiveRecordError
|
229
252
|
end
|
230
253
|
end
|
@@ -19,7 +19,7 @@ module ActiveRecord
|
|
19
19
|
# On the other hand, we want to monitor the performance of our real database
|
20
20
|
# queries, not the performance of the access to the query cache.
|
21
21
|
IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
|
22
|
-
EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
|
22
|
+
EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
|
23
23
|
def ignore_payload?(payload)
|
24
24
|
payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
|
25
25
|
end
|