activerecord 4.1.15 → 4.2.11.3
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 +5 -5
- data/CHANGELOG.md +1162 -1792
- data/README.rdoc +15 -10
- data/lib/active_record.rb +4 -0
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations.rb +158 -49
- 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.rb +26 -13
- 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/preloader.rb +36 -26
- 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/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- 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.rb +56 -94
- 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_set.rb +81 -0
- data/lib/active_record/attribute_set/builder.rb +106 -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.rb +29 -388
- 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/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.rb +71 -46
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/model_schema.rb +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.rb +57 -25
- 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.rb +16 -8
- 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/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- 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 +32 -17
- 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.rb +23 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +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/validations.rb +25 -19
- 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/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
@@ -0,0 +1,66 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeDecorators # :nodoc:
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :attribute_type_decorations, instance_accessor: false # :internal:
|
7
|
+
self.attribute_type_decorations = TypeDecorator.new
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods # :nodoc:
|
11
|
+
def decorate_attribute_type(column_name, decorator_name, &block)
|
12
|
+
matcher = ->(name, _) { name == column_name.to_s }
|
13
|
+
key = "_#{column_name}_#{decorator_name}"
|
14
|
+
decorate_matching_attribute_types(matcher, key, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def decorate_matching_attribute_types(matcher, decorator_name, &block)
|
18
|
+
clear_caches_calculated_from_columns
|
19
|
+
decorator_name = decorator_name.to_s
|
20
|
+
|
21
|
+
# Create new hashes so we don't modify parent classes
|
22
|
+
self.attribute_type_decorations = attribute_type_decorations.merge(decorator_name => [matcher, block])
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def add_user_provided_columns(*)
|
28
|
+
super.map do |column|
|
29
|
+
decorated_type = attribute_type_decorations.apply(column.name, column.cast_type)
|
30
|
+
column.with_type(decorated_type)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class TypeDecorator # :nodoc:
|
36
|
+
delegate :clear, to: :@decorations
|
37
|
+
|
38
|
+
def initialize(decorations = {})
|
39
|
+
@decorations = decorations
|
40
|
+
end
|
41
|
+
|
42
|
+
def merge(*args)
|
43
|
+
TypeDecorator.new(@decorations.merge(*args))
|
44
|
+
end
|
45
|
+
|
46
|
+
def apply(name, type)
|
47
|
+
decorations = decorators_for(name, type)
|
48
|
+
decorations.inject(type) do |new_type, block|
|
49
|
+
block.call(new_type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def decorators_for(name, type)
|
56
|
+
matching(name, type).map(&:last)
|
57
|
+
end
|
58
|
+
|
59
|
+
def matching(name, type)
|
60
|
+
@decorations.values.select do |(matcher, _)|
|
61
|
+
matcher.call(name, type)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/core_ext/enumerable'
|
2
|
+
require 'active_support/core_ext/string/filters'
|
2
3
|
require 'mutex_m'
|
3
4
|
require 'thread_safe'
|
4
5
|
|
@@ -18,6 +19,8 @@ module ActiveRecord
|
|
18
19
|
include TimeZoneConversion
|
19
20
|
include Dirty
|
20
21
|
include Serialization
|
22
|
+
|
23
|
+
delegate :column_for_attribute, to: :class
|
21
24
|
end
|
22
25
|
|
23
26
|
AttrNames = Module.new {
|
@@ -48,7 +51,11 @@ module ActiveRecord
|
|
48
51
|
end
|
49
52
|
|
50
53
|
private
|
51
|
-
|
54
|
+
|
55
|
+
# Override this method in the subclasses for method body.
|
56
|
+
def method_body(method_name, const_name)
|
57
|
+
raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
|
58
|
+
end
|
52
59
|
end
|
53
60
|
|
54
61
|
class GeneratedAttributeMethods < Module; end # :nodoc:
|
@@ -71,7 +78,7 @@ module ActiveRecord
|
|
71
78
|
# accessors, mutators and query methods.
|
72
79
|
def define_attribute_methods # :nodoc:
|
73
80
|
return false if @attribute_methods_generated
|
74
|
-
# Use a mutex; we don't want two
|
81
|
+
# Use a mutex; we don't want two threads simultaneously trying to define
|
75
82
|
# attribute methods.
|
76
83
|
generated_attribute_methods.synchronize do
|
77
84
|
return false if @attribute_methods_generated
|
@@ -155,16 +162,6 @@ module ActiveRecord
|
|
155
162
|
end
|
156
163
|
end
|
157
164
|
|
158
|
-
def find_generated_attribute_method(method_name) # :nodoc:
|
159
|
-
klass = self
|
160
|
-
until klass == Base
|
161
|
-
gen_methods = klass.generated_attribute_methods
|
162
|
-
return gen_methods.instance_method(method_name) if method_defined_within?(method_name, gen_methods, Object)
|
163
|
-
klass = klass.superclass
|
164
|
-
end
|
165
|
-
nil
|
166
|
-
end
|
167
|
-
|
168
165
|
# Returns +true+ if +attribute+ is an attribute method and table exists,
|
169
166
|
# +false+ otherwise.
|
170
167
|
#
|
@@ -193,24 +190,29 @@ module ActiveRecord
|
|
193
190
|
[]
|
194
191
|
end
|
195
192
|
end
|
196
|
-
end
|
197
193
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
194
|
+
# Returns the column object for the named attribute.
|
195
|
+
# Returns nil if the named attribute does not exist.
|
196
|
+
#
|
197
|
+
# class Person < ActiveRecord::Base
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# person = Person.new
|
201
|
+
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
202
|
+
# # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
203
|
+
#
|
204
|
+
# person.column_for_attribute(:nothing)
|
205
|
+
# # => nil
|
206
|
+
def column_for_attribute(name)
|
207
|
+
column = columns_hash[name.to_s]
|
208
|
+
if column.nil?
|
209
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
210
|
+
`#column_for_attribute` will return a null object for non-existent
|
211
|
+
columns in Rails 5. Use `#has_attribute?` if you need to check for
|
212
|
+
an attribute's existence.
|
213
|
+
MSG
|
211
214
|
end
|
212
|
-
|
213
|
-
super
|
215
|
+
column
|
214
216
|
end
|
215
217
|
end
|
216
218
|
|
@@ -231,18 +233,14 @@ module ActiveRecord
|
|
231
233
|
# person.respond_to('age?') # => true
|
232
234
|
# person.respond_to(:nothing) # => false
|
233
235
|
def respond_to?(name, include_private = false)
|
236
|
+
return false unless super
|
234
237
|
name = name.to_s
|
235
|
-
self.class.define_attribute_methods
|
236
|
-
result = super
|
237
|
-
|
238
|
-
# If the result is false the answer is false.
|
239
|
-
return false unless result
|
240
238
|
|
241
239
|
# If the result is true then check for the select case.
|
242
240
|
# For queries selecting a subset of columns, return false for unselected columns.
|
243
241
|
# We check defined?(@attributes) not to issue warnings if called on objects that
|
244
242
|
# have been allocated but not yet initialized.
|
245
|
-
if defined?(@attributes) &&
|
243
|
+
if defined?(@attributes) && self.class.column_names.include?(name)
|
246
244
|
return has_attribute?(name)
|
247
245
|
end
|
248
246
|
|
@@ -259,7 +257,7 @@ module ActiveRecord
|
|
259
257
|
# person.has_attribute?('age') # => true
|
260
258
|
# person.has_attribute?(:nothing) # => false
|
261
259
|
def has_attribute?(attr_name)
|
262
|
-
@attributes.
|
260
|
+
@attributes.key?(attr_name.to_s)
|
263
261
|
end
|
264
262
|
|
265
263
|
# Returns an array of names for the attributes available on this object.
|
@@ -283,22 +281,14 @@ module ActiveRecord
|
|
283
281
|
# person.attributes
|
284
282
|
# # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}
|
285
283
|
def attributes
|
286
|
-
|
287
|
-
attrs[name] = read_attribute(name)
|
288
|
-
}
|
289
|
-
end
|
290
|
-
|
291
|
-
# Placeholder so it can be overriden when needed by serialization
|
292
|
-
def attributes_for_coder # :nodoc:
|
293
|
-
attributes
|
284
|
+
@attributes.to_hash
|
294
285
|
end
|
295
286
|
|
296
287
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
297
|
-
# attribute +attr_name+. String attributes are truncated
|
288
|
+
# attribute +attr_name+. String attributes are truncated up to 50
|
298
289
|
# characters, Date and Time attributes are returned in the
|
299
|
-
# <tt>:db</tt> format
|
300
|
-
#
|
301
|
-
# modification.
|
290
|
+
# <tt>:db</tt> format. Other attributes return the value of
|
291
|
+
# <tt>#inspect</tt> without modification.
|
302
292
|
#
|
303
293
|
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
304
294
|
#
|
@@ -309,7 +299,7 @@ module ActiveRecord
|
|
309
299
|
# # => "\"2012-10-22 00:15:07\""
|
310
300
|
#
|
311
301
|
# person.attribute_for_inspect(:tag_ids)
|
312
|
-
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
302
|
+
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
313
303
|
def attribute_for_inspect(attr_name)
|
314
304
|
value = read_attribute(attr_name)
|
315
305
|
|
@@ -317,9 +307,6 @@ module ActiveRecord
|
|
317
307
|
"#{value[0, 50]}...".inspect
|
318
308
|
elsif value.is_a?(Date) || value.is_a?(Time)
|
319
309
|
%("#{value.to_s(:db)}")
|
320
|
-
elsif value.is_a?(Array) && value.size > 10
|
321
|
-
inspected = value.first(10).inspect
|
322
|
-
%(#{inspected[0...-1]}, ...])
|
323
310
|
else
|
324
311
|
value.inspect
|
325
312
|
end
|
@@ -333,39 +320,24 @@ module ActiveRecord
|
|
333
320
|
# class Task < ActiveRecord::Base
|
334
321
|
# end
|
335
322
|
#
|
336
|
-
#
|
337
|
-
#
|
338
|
-
#
|
339
|
-
#
|
340
|
-
#
|
341
|
-
#
|
342
|
-
#
|
323
|
+
# task = Task.new(title: '', is_done: false)
|
324
|
+
# task.attribute_present?(:title) # => false
|
325
|
+
# task.attribute_present?(:is_done) # => true
|
326
|
+
# task.title = 'Buy milk'
|
327
|
+
# task.is_done = true
|
328
|
+
# task.attribute_present?(:title) # => true
|
329
|
+
# task.attribute_present?(:is_done) # => true
|
343
330
|
def attribute_present?(attribute)
|
344
|
-
value =
|
331
|
+
value = _read_attribute(attribute)
|
345
332
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
346
333
|
end
|
347
334
|
|
348
|
-
# Returns the column object for the named attribute. Returns +nil+ if the
|
349
|
-
# named attribute not exists.
|
350
|
-
#
|
351
|
-
# class Person < ActiveRecord::Base
|
352
|
-
# end
|
353
|
-
#
|
354
|
-
# person = Person.new
|
355
|
-
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
356
|
-
# # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
357
|
-
#
|
358
|
-
# person.column_for_attribute(:nothing)
|
359
|
-
# # => nil
|
360
|
-
def column_for_attribute(name)
|
361
|
-
# FIXME: should this return a null object for columns that don't exist?
|
362
|
-
self.class.columns_hash[name.to_s]
|
363
|
-
end
|
364
|
-
|
365
335
|
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
366
336
|
# "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
|
367
337
|
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
|
368
338
|
#
|
339
|
+
# Note: +:id+ is always present.
|
340
|
+
#
|
369
341
|
# Alias for the <tt>read_attribute</tt> method.
|
370
342
|
#
|
371
343
|
# class Person < ActiveRecord::Base
|
@@ -392,20 +364,13 @@ module ActiveRecord
|
|
392
364
|
# person = Person.new
|
393
365
|
# person[:age] = '22'
|
394
366
|
# person[:age] # => 22
|
395
|
-
# person[:age] # =>
|
367
|
+
# person[:age].class # => Integer
|
396
368
|
def []=(attr_name, value)
|
397
369
|
write_attribute(attr_name, value)
|
398
370
|
end
|
399
371
|
|
400
372
|
protected
|
401
373
|
|
402
|
-
def clone_attributes(reader_method = :read_attribute, attributes = {}) # :nodoc:
|
403
|
-
attribute_names.each do |name|
|
404
|
-
attributes[name] = clone_attribute_value(reader_method, name)
|
405
|
-
end
|
406
|
-
attributes
|
407
|
-
end
|
408
|
-
|
409
374
|
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
|
410
375
|
value = send(reader_method, attribute_name)
|
411
376
|
value.duplicable? ? value.clone : value
|
@@ -423,7 +388,7 @@ module ActiveRecord
|
|
423
388
|
|
424
389
|
def attribute_method?(attr_name) # :nodoc:
|
425
390
|
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
426
|
-
defined?(@attributes) && @attributes.
|
391
|
+
defined?(@attributes) && @attributes.key?(attr_name)
|
427
392
|
end
|
428
393
|
|
429
394
|
private
|
@@ -442,16 +407,16 @@ module ActiveRecord
|
|
442
407
|
|
443
408
|
# Filters the primary keys and readonly attributes from the attribute names.
|
444
409
|
def attributes_for_update(attribute_names)
|
445
|
-
attribute_names.
|
446
|
-
|
410
|
+
attribute_names.reject do |name|
|
411
|
+
readonly_attribute?(name)
|
447
412
|
end
|
448
413
|
end
|
449
414
|
|
450
415
|
# Filters out the primary keys, from the attribute names, when the primary
|
451
416
|
# key is to be generated (e.g. the id attribute has no value).
|
452
417
|
def attributes_for_create(attribute_names)
|
453
|
-
attribute_names.
|
454
|
-
|
418
|
+
attribute_names.reject do |name|
|
419
|
+
pk_attribute?(name) && id.nil?
|
455
420
|
end
|
456
421
|
end
|
457
422
|
|
@@ -460,14 +425,11 @@ module ActiveRecord
|
|
460
425
|
end
|
461
426
|
|
462
427
|
def pk_attribute?(name)
|
463
|
-
|
428
|
+
name == self.class.primary_key
|
464
429
|
end
|
465
430
|
|
466
431
|
def typecasted_attribute_value(name)
|
467
|
-
|
468
|
-
# If the values stored in @attributes were already typecasted, this code
|
469
|
-
# could be simplified
|
470
|
-
read_attribute(name)
|
432
|
+
_read_attribute(name)
|
471
433
|
end
|
472
434
|
end
|
473
435
|
end
|
@@ -28,6 +28,7 @@ module ActiveRecord
|
|
28
28
|
|
29
29
|
included do
|
30
30
|
attribute_method_suffix "_before_type_cast"
|
31
|
+
attribute_method_suffix "_came_from_user?"
|
31
32
|
end
|
32
33
|
|
33
34
|
# Returns the value of the attribute identified by +attr_name+ before
|
@@ -43,7 +44,7 @@ module ActiveRecord
|
|
43
44
|
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
44
45
|
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
|
45
46
|
def read_attribute_before_type_cast(attr_name)
|
46
|
-
@attributes[attr_name.to_s]
|
47
|
+
@attributes[attr_name.to_s].value_before_type_cast
|
47
48
|
end
|
48
49
|
|
49
50
|
# Returns a hash of attributes before typecasting and deserialization.
|
@@ -57,7 +58,7 @@ module ActiveRecord
|
|
57
58
|
# task.attributes_before_type_cast
|
58
59
|
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
|
59
60
|
def attributes_before_type_cast
|
60
|
-
@attributes
|
61
|
+
@attributes.values_before_type_cast
|
61
62
|
end
|
62
63
|
|
63
64
|
private
|
@@ -66,6 +67,10 @@ module ActiveRecord
|
|
66
67
|
def attribute_before_type_cast(attribute_name)
|
67
68
|
read_attribute_before_type_cast(attribute_name)
|
68
69
|
end
|
70
|
+
|
71
|
+
def attribute_came_from_user?(attribute_name)
|
72
|
+
@attributes[attribute_name].came_from_user?
|
73
|
+
end
|
69
74
|
end
|
70
75
|
end
|
71
76
|
end
|
@@ -34,28 +34,57 @@ module ActiveRecord
|
|
34
34
|
# <tt>reload</tt> the record and clears changed attributes.
|
35
35
|
def reload(*)
|
36
36
|
super.tap do
|
37
|
-
|
37
|
+
clear_changes_information
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
def initialize_dup(other) # :nodoc:
|
42
|
+
super
|
43
|
+
@original_raw_attributes = nil
|
44
|
+
calculate_changes_from_defaults
|
45
|
+
end
|
46
|
+
|
47
|
+
def changes_applied
|
48
|
+
super
|
49
|
+
store_original_raw_attributes
|
50
|
+
end
|
45
51
|
|
46
|
-
|
47
|
-
def initialize_internals_callback
|
52
|
+
def clear_changes_information
|
48
53
|
super
|
49
|
-
|
54
|
+
original_raw_attributes.clear
|
55
|
+
end
|
56
|
+
|
57
|
+
def changed_attributes
|
58
|
+
# This should only be set by methods which will call changed_attributes
|
59
|
+
# multiple times when it is known that the computed value cannot change.
|
60
|
+
if defined?(@cached_changed_attributes)
|
61
|
+
@cached_changed_attributes
|
62
|
+
else
|
63
|
+
super.reverse_merge(attributes_changed_in_place).freeze
|
64
|
+
end
|
50
65
|
end
|
51
66
|
|
52
|
-
def
|
67
|
+
def changes
|
68
|
+
cache_changed_attributes do
|
69
|
+
super
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def attribute_changed_in_place?(attr_name)
|
74
|
+
old_value = original_raw_attribute(attr_name)
|
75
|
+
@attributes[attr_name].changed_in_place_from?(old_value)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def changes_include?(attr_name)
|
81
|
+
super || attribute_changed_in_place?(attr_name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def calculate_changes_from_defaults
|
53
85
|
@changed_attributes = nil
|
54
|
-
|
55
|
-
|
56
|
-
self.class.columns.each do |c|
|
57
|
-
attr, orig_value = c.name, c.default
|
58
|
-
changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
|
86
|
+
self.class.column_defaults.each do |attr, orig_value|
|
87
|
+
set_attribute_was(attr, orig_value) if _field_changed?(attr, orig_value)
|
59
88
|
end
|
60
89
|
end
|
61
90
|
|
@@ -63,19 +92,36 @@ module ActiveRecord
|
|
63
92
|
def write_attribute(attr, value)
|
64
93
|
attr = attr.to_s
|
65
94
|
|
66
|
-
|
95
|
+
old_value = old_attribute_value(attr)
|
96
|
+
|
97
|
+
result = super
|
98
|
+
store_original_raw_attribute(attr)
|
99
|
+
save_changed_attribute(attr, old_value)
|
100
|
+
result
|
101
|
+
end
|
102
|
+
|
103
|
+
def raw_write_attribute(attr, value)
|
104
|
+
attr = attr.to_s
|
105
|
+
|
106
|
+
result = super
|
107
|
+
original_raw_attributes[attr] = value
|
108
|
+
result
|
109
|
+
end
|
67
110
|
|
68
|
-
|
111
|
+
def save_changed_attribute(attr, old_value)
|
112
|
+
clear_changed_attributes_cache
|
113
|
+
if attribute_changed_by_setter?(attr)
|
114
|
+
clear_attribute_changes(attr) unless _field_changed?(attr, old_value)
|
115
|
+
else
|
116
|
+
set_attribute_was(attr, old_value) if _field_changed?(attr, old_value)
|
117
|
+
end
|
69
118
|
end
|
70
119
|
|
71
|
-
def
|
72
|
-
# The attribute already has an unsaved change.
|
120
|
+
def old_attribute_value(attr)
|
73
121
|
if attribute_changed?(attr)
|
74
|
-
|
75
|
-
changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
|
122
|
+
changed_attributes[attr]
|
76
123
|
else
|
77
|
-
|
78
|
-
changed_attributes[attr] = old if _field_changed?(attr, old, value)
|
124
|
+
clone_attribute_value(:_read_attribute, attr)
|
79
125
|
end
|
80
126
|
end
|
81
127
|
|
@@ -90,37 +136,55 @@ module ActiveRecord
|
|
90
136
|
# Serialized attributes should always be written in case they've been
|
91
137
|
# changed in place.
|
92
138
|
def keys_for_partial_write
|
93
|
-
changed
|
139
|
+
changed & persistable_attribute_names
|
94
140
|
end
|
95
141
|
|
96
|
-
def _field_changed?(attr,
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
142
|
+
def _field_changed?(attr, old_value)
|
143
|
+
@attributes[attr].changed_from?(old_value)
|
144
|
+
end
|
145
|
+
|
146
|
+
def attributes_changed_in_place
|
147
|
+
changed_in_place.each_with_object({}) do |attr_name, h|
|
148
|
+
orig = @attributes[attr_name].original_value
|
149
|
+
h[attr_name] = orig
|
104
150
|
end
|
151
|
+
end
|
105
152
|
|
106
|
-
|
153
|
+
def changed_in_place
|
154
|
+
self.class.attribute_names.select do |attr_name|
|
155
|
+
attribute_changed_in_place?(attr_name)
|
156
|
+
end
|
107
157
|
end
|
108
158
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
159
|
+
def original_raw_attribute(attr_name)
|
160
|
+
original_raw_attributes.fetch(attr_name) do
|
161
|
+
read_attribute_before_type_cast(attr_name)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def original_raw_attributes
|
166
|
+
@original_raw_attributes ||= {}
|
167
|
+
end
|
168
|
+
|
169
|
+
def store_original_raw_attribute(attr_name)
|
170
|
+
original_raw_attributes[attr_name] = @attributes[attr_name].value_for_database rescue nil
|
171
|
+
end
|
172
|
+
|
173
|
+
def store_original_raw_attributes
|
174
|
+
attribute_names.each do |attr|
|
175
|
+
store_original_raw_attribute(attr)
|
176
|
+
end
|
115
177
|
end
|
116
178
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
179
|
+
def cache_changed_attributes
|
180
|
+
@cached_changed_attributes = changed_attributes
|
181
|
+
yield
|
182
|
+
ensure
|
183
|
+
clear_changed_attributes_cache
|
120
184
|
end
|
121
185
|
|
122
|
-
def
|
123
|
-
|
186
|
+
def clear_changed_attributes_cache
|
187
|
+
remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
|
124
188
|
end
|
125
189
|
end
|
126
190
|
end
|