activerecord 4.1.15 → 4.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +634 -2176
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/attribute.rb +131 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +22 -32
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +51 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/validations/uniqueness.rb +9 -23
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record.rb +2 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +71 -14
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -18,6 +18,8 @@ module ActiveRecord
|
|
18
18
|
include TimeZoneConversion
|
19
19
|
include Dirty
|
20
20
|
include Serialization
|
21
|
+
|
22
|
+
delegate :column_for_attribute, to: :class
|
21
23
|
end
|
22
24
|
|
23
25
|
AttrNames = Module.new {
|
@@ -29,7 +31,7 @@ module ActiveRecord
|
|
29
31
|
end
|
30
32
|
}
|
31
33
|
|
32
|
-
BLACKLISTED_CLASS_METHODS = %w(private public protected
|
34
|
+
BLACKLISTED_CLASS_METHODS = %w(private public protected)
|
33
35
|
|
34
36
|
class AttributeMethodCache
|
35
37
|
def initialize
|
@@ -48,7 +50,11 @@ module ActiveRecord
|
|
48
50
|
end
|
49
51
|
|
50
52
|
private
|
51
|
-
|
53
|
+
|
54
|
+
# Override this method in the subclasses for method body.
|
55
|
+
def method_body(method_name, const_name)
|
56
|
+
raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
|
57
|
+
end
|
52
58
|
end
|
53
59
|
|
54
60
|
class GeneratedAttributeMethods < Module; end # :nodoc:
|
@@ -63,15 +69,13 @@ module ActiveRecord
|
|
63
69
|
@generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
|
64
70
|
@attribute_methods_generated = false
|
65
71
|
include @generated_attribute_methods
|
66
|
-
|
67
|
-
super
|
68
72
|
end
|
69
73
|
|
70
74
|
# Generates all the attribute related methods for columns in the database
|
71
75
|
# accessors, mutators and query methods.
|
72
76
|
def define_attribute_methods # :nodoc:
|
73
77
|
return false if @attribute_methods_generated
|
74
|
-
# Use a mutex; we don't want two
|
78
|
+
# Use a mutex; we don't want two threads simultaneously trying to define
|
75
79
|
# attribute methods.
|
76
80
|
generated_attribute_methods.synchronize do
|
77
81
|
return false if @attribute_methods_generated
|
@@ -84,7 +88,7 @@ module ActiveRecord
|
|
84
88
|
|
85
89
|
def undefine_attribute_methods # :nodoc:
|
86
90
|
generated_attribute_methods.synchronize do
|
87
|
-
super if
|
91
|
+
super if @attribute_methods_generated
|
88
92
|
@attribute_methods_generated = false
|
89
93
|
end
|
90
94
|
end
|
@@ -105,7 +109,7 @@ module ActiveRecord
|
|
105
109
|
# # => false
|
106
110
|
def instance_method_already_implemented?(method_name)
|
107
111
|
if dangerous_attribute_method?(method_name)
|
108
|
-
raise DangerousAttributeError, "#{method_name} is defined by Active Record
|
112
|
+
raise DangerousAttributeError, "#{method_name} is defined by Active Record"
|
109
113
|
end
|
110
114
|
|
111
115
|
if superclass == Base
|
@@ -155,16 +159,6 @@ module ActiveRecord
|
|
155
159
|
end
|
156
160
|
end
|
157
161
|
|
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
162
|
# Returns +true+ if +attribute+ is an attribute method and table exists,
|
169
163
|
# +false+ otherwise.
|
170
164
|
#
|
@@ -193,24 +187,29 @@ module ActiveRecord
|
|
193
187
|
[]
|
194
188
|
end
|
195
189
|
end
|
196
|
-
end
|
197
190
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
191
|
+
# Returns the column object for the named attribute.
|
192
|
+
# Returns nil if the named attribute does not exist.
|
193
|
+
#
|
194
|
+
# class Person < ActiveRecord::Base
|
195
|
+
# end
|
196
|
+
#
|
197
|
+
# person = Person.new
|
198
|
+
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
199
|
+
# # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
200
|
+
#
|
201
|
+
# person.column_for_attribute(:nothing)
|
202
|
+
# # => nil
|
203
|
+
def column_for_attribute(name)
|
204
|
+
column = columns_hash[name.to_s]
|
205
|
+
if column.nil?
|
206
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
|
207
|
+
`column_for_attribute` will return a null object for non-existent columns
|
208
|
+
in Rails 5.0. Use `has_attribute?` if you need to check for an
|
209
|
+
attribute's existence.
|
210
|
+
MESSAGE
|
211
211
|
end
|
212
|
-
|
213
|
-
super
|
212
|
+
column
|
214
213
|
end
|
215
214
|
end
|
216
215
|
|
@@ -231,18 +230,14 @@ module ActiveRecord
|
|
231
230
|
# person.respond_to('age?') # => true
|
232
231
|
# person.respond_to(:nothing) # => false
|
233
232
|
def respond_to?(name, include_private = false)
|
233
|
+
return false unless super
|
234
234
|
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
235
|
|
241
236
|
# If the result is true then check for the select case.
|
242
237
|
# For queries selecting a subset of columns, return false for unselected columns.
|
243
238
|
# We check defined?(@attributes) not to issue warnings if called on objects that
|
244
239
|
# have been allocated but not yet initialized.
|
245
|
-
if defined?(@attributes) &&
|
240
|
+
if defined?(@attributes) && self.class.column_names.include?(name)
|
246
241
|
return has_attribute?(name)
|
247
242
|
end
|
248
243
|
|
@@ -259,7 +254,7 @@ module ActiveRecord
|
|
259
254
|
# person.has_attribute?('age') # => true
|
260
255
|
# person.has_attribute?(:nothing) # => false
|
261
256
|
def has_attribute?(attr_name)
|
262
|
-
@attributes.
|
257
|
+
@attributes.key?(attr_name.to_s)
|
263
258
|
end
|
264
259
|
|
265
260
|
# Returns an array of names for the attributes available on this object.
|
@@ -283,20 +278,13 @@ module ActiveRecord
|
|
283
278
|
# person.attributes
|
284
279
|
# # => {"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
280
|
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
|
281
|
+
@attributes.to_hash
|
294
282
|
end
|
295
283
|
|
296
284
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
297
|
-
# attribute +attr_name+. String attributes are truncated
|
285
|
+
# attribute +attr_name+. String attributes are truncated up to 50
|
298
286
|
# characters, Date and Time attributes are returned in the
|
299
|
-
# <tt>:db</tt> format, Array attributes are truncated
|
287
|
+
# <tt>:db</tt> format, Array attributes are truncated up to 10 values.
|
300
288
|
# Other attributes return the value of <tt>#inspect</tt> without
|
301
289
|
# modification.
|
302
290
|
#
|
@@ -333,39 +321,24 @@ module ActiveRecord
|
|
333
321
|
# class Task < ActiveRecord::Base
|
334
322
|
# end
|
335
323
|
#
|
336
|
-
#
|
337
|
-
#
|
338
|
-
#
|
339
|
-
#
|
340
|
-
#
|
341
|
-
#
|
342
|
-
#
|
324
|
+
# task = Task.new(title: '', is_done: false)
|
325
|
+
# task.attribute_present?(:title) # => false
|
326
|
+
# task.attribute_present?(:is_done) # => true
|
327
|
+
# task.title = 'Buy milk'
|
328
|
+
# task.is_done = true
|
329
|
+
# task.attribute_present?(:title) # => true
|
330
|
+
# task.attribute_present?(:is_done) # => true
|
343
331
|
def attribute_present?(attribute)
|
344
332
|
value = read_attribute(attribute)
|
345
333
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
346
334
|
end
|
347
335
|
|
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
336
|
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
366
337
|
# "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
|
367
338
|
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
|
368
339
|
#
|
340
|
+
# Note: +:id+ is always present.
|
341
|
+
#
|
369
342
|
# Alias for the <tt>read_attribute</tt> method.
|
370
343
|
#
|
371
344
|
# class Person < ActiveRecord::Base
|
@@ -399,13 +372,6 @@ module ActiveRecord
|
|
399
372
|
|
400
373
|
protected
|
401
374
|
|
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
375
|
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
|
410
376
|
value = send(reader_method, attribute_name)
|
411
377
|
value.duplicable? ? value.clone : value
|
@@ -423,7 +389,7 @@ module ActiveRecord
|
|
423
389
|
|
424
390
|
def attribute_method?(attr_name) # :nodoc:
|
425
391
|
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
426
|
-
defined?(@attributes) && @attributes.
|
392
|
+
defined?(@attributes) && @attributes.key?(attr_name)
|
427
393
|
end
|
428
394
|
|
429
395
|
private
|
@@ -442,16 +408,16 @@ module ActiveRecord
|
|
442
408
|
|
443
409
|
# Filters the primary keys and readonly attributes from the attribute names.
|
444
410
|
def attributes_for_update(attribute_names)
|
445
|
-
attribute_names.
|
446
|
-
|
411
|
+
attribute_names.reject do |name|
|
412
|
+
readonly_attribute?(name)
|
447
413
|
end
|
448
414
|
end
|
449
415
|
|
450
416
|
# Filters out the primary keys, from the attribute names, when the primary
|
451
417
|
# key is to be generated (e.g. the id attribute has no value).
|
452
418
|
def attributes_for_create(attribute_names)
|
453
|
-
attribute_names.
|
454
|
-
|
419
|
+
attribute_names.reject do |name|
|
420
|
+
pk_attribute?(name) && id.nil?
|
455
421
|
end
|
456
422
|
end
|
457
423
|
|
@@ -460,13 +426,10 @@ module ActiveRecord
|
|
460
426
|
end
|
461
427
|
|
462
428
|
def pk_attribute?(name)
|
463
|
-
|
429
|
+
name == self.class.primary_key
|
464
430
|
end
|
465
431
|
|
466
432
|
def typecasted_attribute_value(name)
|
467
|
-
# FIXME: we need @attributes to be used consistently.
|
468
|
-
# If the values stored in @attributes were already typecasted, this code
|
469
|
-
# could be simplified
|
470
433
|
read_attribute(name)
|
471
434
|
end
|
472
435
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class AttributeSet # :nodoc:
|
3
|
+
class Builder # :nodoc:
|
4
|
+
attr_reader :types
|
5
|
+
|
6
|
+
def initialize(types)
|
7
|
+
@types = types
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_from_database(values = {}, additional_types = {})
|
11
|
+
attributes = build_attributes_from_values(values, additional_types)
|
12
|
+
add_uninitialized_attributes(attributes)
|
13
|
+
AttributeSet.new(attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def build_attributes_from_values(values, additional_types)
|
19
|
+
values.each_with_object({}) do |(name, value), hash|
|
20
|
+
type = additional_types.fetch(name, types[name])
|
21
|
+
hash[name] = Attribute.from_database(name, value, type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_uninitialized_attributes(attributes)
|
26
|
+
types.except(*attributes.keys).each do |name, type|
|
27
|
+
attributes[name] = Attribute.uninitialized(name, type)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'active_record/attribute_set/builder'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class AttributeSet # :nodoc:
|
5
|
+
delegate :keys, to: :initialized_attributes
|
6
|
+
|
7
|
+
def initialize(attributes)
|
8
|
+
@attributes = attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](name)
|
12
|
+
attributes[name] || Attribute.null(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def values_before_type_cast
|
16
|
+
attributes.transform_values(&:value_before_type_cast)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
initialized_attributes.transform_values(&:value)
|
21
|
+
end
|
22
|
+
alias_method :to_h, :to_hash
|
23
|
+
|
24
|
+
def key?(name)
|
25
|
+
attributes.key?(name) && self[name].initialized?
|
26
|
+
end
|
27
|
+
|
28
|
+
def fetch_value(name, &block)
|
29
|
+
self[name].value(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_from_database(name, value)
|
33
|
+
attributes[name] = self[name].with_value_from_database(value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def write_from_user(name, value)
|
37
|
+
attributes[name] = self[name].with_value_from_user(value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def freeze
|
41
|
+
@attributes.freeze
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize_dup(_)
|
46
|
+
@attributes = attributes.transform_values(&:dup)
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize_clone(_)
|
51
|
+
@attributes = attributes.clone
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
def reset(key)
|
56
|
+
if key?(key)
|
57
|
+
write_from_database(key, nil)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def ensure_initialized(key)
|
62
|
+
unless self[key].initialized?
|
63
|
+
write_from_database(key, nil)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
attr_reader :attributes
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def initialized_attributes
|
74
|
+
attributes.select { |_, attr| attr.initialized? }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Attributes # :nodoc:
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
Type = ActiveRecord::Type
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :user_provided_columns, instance_accessor: false # :internal:
|
9
|
+
self.user_provided_columns = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods # :nodoc:
|
13
|
+
# Defines or overrides a attribute on this model. This allows customization of
|
14
|
+
# Active Record's type casting behavior, as well as adding support for user defined
|
15
|
+
# types.
|
16
|
+
#
|
17
|
+
# +name+ The name of the methods to define attribute methods for, and the column which
|
18
|
+
# this will persist to.
|
19
|
+
#
|
20
|
+
# +cast_type+ A type object that contains information about how to type cast the value.
|
21
|
+
# See the examples section for more information.
|
22
|
+
#
|
23
|
+
# ==== Options
|
24
|
+
# The options hash accepts the following options:
|
25
|
+
#
|
26
|
+
# +default+ is the default value that the column should use on a new record.
|
27
|
+
#
|
28
|
+
# ==== Examples
|
29
|
+
#
|
30
|
+
# The type detected by Active Record can be overridden.
|
31
|
+
#
|
32
|
+
# # db/schema.rb
|
33
|
+
# create_table :store_listings, force: true do |t|
|
34
|
+
# t.decimal :price_in_cents
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# # app/models/store_listing.rb
|
38
|
+
# class StoreListing < ActiveRecord::Base
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# store_listing = StoreListing.new(price_in_cents: '10.1')
|
42
|
+
#
|
43
|
+
# # before
|
44
|
+
# store_listing.price_in_cents # => BigDecimal.new(10.1)
|
45
|
+
#
|
46
|
+
# class StoreListing < ActiveRecord::Base
|
47
|
+
# attribute :price_in_cents, Type::Integer.new
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# # after
|
51
|
+
# store_listing.price_in_cents # => 10
|
52
|
+
#
|
53
|
+
# Users may also define their own custom types, as long as they respond to the methods
|
54
|
+
# defined on the value type. The `type_cast` method on your type object will be called
|
55
|
+
# with values both from the database, and from your controllers. See
|
56
|
+
# `ActiveRecord::Attributes::Type::Value` for the expected API. It is recommended that your
|
57
|
+
# type objects inherit from an existing type, or the base value type.
|
58
|
+
#
|
59
|
+
# class MoneyType < ActiveRecord::Type::Integer
|
60
|
+
# def type_cast(value)
|
61
|
+
# if value.include?('$')
|
62
|
+
# price_in_dollars = value.gsub(/\$/, '').to_f
|
63
|
+
# price_in_dollars * 100
|
64
|
+
# else
|
65
|
+
# value.to_i
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# class StoreListing < ActiveRecord::Base
|
71
|
+
# attribute :price_in_cents, MoneyType.new
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# store_listing = StoreListing.new(price_in_cents: '$10.00')
|
75
|
+
# store_listing.price_in_cents # => 1000
|
76
|
+
def attribute(name, cast_type, options = {})
|
77
|
+
name = name.to_s
|
78
|
+
clear_caches_calculated_from_columns
|
79
|
+
# Assign a new hash to ensure that subclasses do not share a hash
|
80
|
+
self.user_provided_columns = user_provided_columns.merge(name => connection.new_column(name, options[:default], cast_type))
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns an array of column objects for the table associated with this class.
|
84
|
+
def columns
|
85
|
+
@columns ||= add_user_provided_columns(connection.schema_cache.columns(table_name))
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns a hash of column objects for the table associated with this class.
|
89
|
+
def columns_hash
|
90
|
+
@columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
|
91
|
+
end
|
92
|
+
|
93
|
+
def reset_column_information # :nodoc:
|
94
|
+
super
|
95
|
+
clear_caches_calculated_from_columns
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def add_user_provided_columns(schema_columns)
|
101
|
+
existing_columns = schema_columns.map do |column|
|
102
|
+
user_provided_columns[column.name] || column
|
103
|
+
end
|
104
|
+
|
105
|
+
existing_column_names = existing_columns.map(&:name)
|
106
|
+
new_columns = user_provided_columns.except(*existing_column_names).values
|
107
|
+
|
108
|
+
existing_columns + new_columns
|
109
|
+
end
|
110
|
+
|
111
|
+
def clear_caches_calculated_from_columns
|
112
|
+
@attributes_builder = nil
|
113
|
+
@column_names = nil
|
114
|
+
@column_types = nil
|
115
|
+
@columns = nil
|
116
|
+
@columns_hash = nil
|
117
|
+
@content_columns = nil
|
118
|
+
@default_attributes = nil
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -177,15 +177,15 @@ module ActiveRecord
|
|
177
177
|
# before actually defining them.
|
178
178
|
def add_autosave_association_callbacks(reflection)
|
179
179
|
save_method = :"autosave_associated_records_for_#{reflection.name}"
|
180
|
+
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
181
|
+
collection = reflection.collection?
|
180
182
|
|
181
|
-
if
|
183
|
+
if collection
|
182
184
|
before_save :before_save_collection_association
|
183
185
|
|
184
186
|
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
|
185
|
-
|
186
|
-
|
187
|
-
after_update save_method
|
188
|
-
elsif reflection.macro == :has_one
|
187
|
+
after_save save_method
|
188
|
+
elsif reflection.has_one?
|
189
189
|
define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
|
190
190
|
# Configures two callbacks instead of a single after_save so that
|
191
191
|
# the model may rely on their execution order relative to its
|
@@ -202,22 +202,9 @@ module ActiveRecord
|
|
202
202
|
before_save save_method
|
203
203
|
end
|
204
204
|
|
205
|
-
define_autosave_validation_callbacks(reflection)
|
206
|
-
end
|
207
|
-
|
208
|
-
def define_autosave_validation_callbacks(reflection)
|
209
|
-
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
210
205
|
if reflection.validate? && !method_defined?(validation_method)
|
211
|
-
|
212
|
-
|
213
|
-
else
|
214
|
-
method = :validate_single_association
|
215
|
-
end
|
216
|
-
|
217
|
-
define_non_cyclic_method(validation_method) do
|
218
|
-
send(method, reflection)
|
219
|
-
true
|
220
|
-
end
|
206
|
+
method = (collection ? :validate_collection_association : :validate_single_association)
|
207
|
+
define_non_cyclic_method(validation_method) { send(method, reflection) }
|
221
208
|
validate validation_method
|
222
209
|
end
|
223
210
|
end
|
@@ -316,7 +303,8 @@ module ActiveRecord
|
|
316
303
|
def association_valid?(reflection, record)
|
317
304
|
return true if record.destroyed? || record.marked_for_destruction?
|
318
305
|
|
319
|
-
|
306
|
+
validation_context = self.validation_context unless [:create, :update].include?(self.validation_context)
|
307
|
+
unless valid = record.valid?(validation_context)
|
320
308
|
if reflection.options[:autosave]
|
321
309
|
record.errors.each do |attribute, message|
|
322
310
|
attribute = "#{reflection.name}.#{attribute}"
|
@@ -350,6 +338,7 @@ module ActiveRecord
|
|
350
338
|
autosave = reflection.options[:autosave]
|
351
339
|
|
352
340
|
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
|
341
|
+
|
353
342
|
if autosave
|
354
343
|
records_to_destroy = records.select(&:marked_for_destruction?)
|
355
344
|
records_to_destroy.each { |record| association.destroy(record) }
|
@@ -373,6 +362,7 @@ module ActiveRecord
|
|
373
362
|
|
374
363
|
raise ActiveRecord::Rollback unless saved
|
375
364
|
end
|
365
|
+
@new_record_before_save = false
|
376
366
|
end
|
377
367
|
|
378
368
|
# reconstruct the scope now that we know the owner's id
|
data/lib/active_record/base.rb
CHANGED
@@ -9,16 +9,19 @@ require 'active_support/core_ext/class/delegating_attributes'
|
|
9
9
|
require 'active_support/core_ext/array/extract_options'
|
10
10
|
require 'active_support/core_ext/hash/deep_merge'
|
11
11
|
require 'active_support/core_ext/hash/slice'
|
12
|
+
require 'active_support/core_ext/hash/transform_values'
|
12
13
|
require 'active_support/core_ext/string/behavior'
|
13
14
|
require 'active_support/core_ext/kernel/singleton_class'
|
14
15
|
require 'active_support/core_ext/module/introspection'
|
15
16
|
require 'active_support/core_ext/object/duplicable'
|
16
17
|
require 'active_support/core_ext/class/subclasses'
|
17
18
|
require 'arel'
|
19
|
+
require 'active_record/attribute_decorators'
|
18
20
|
require 'active_record/errors'
|
19
21
|
require 'active_record/log_subscriber'
|
20
22
|
require 'active_record/explain_subscriber'
|
21
23
|
require 'active_record/relation/delegation'
|
24
|
+
require 'active_record/attributes'
|
22
25
|
|
23
26
|
module ActiveRecord #:nodoc:
|
24
27
|
# = Active Record
|
@@ -138,6 +141,7 @@ module ActiveRecord #:nodoc:
|
|
138
141
|
#
|
139
142
|
# In addition to the basic accessors, query methods are also automatically available on the Active Record object.
|
140
143
|
# Query methods allow you to test whether an attribute value is present.
|
144
|
+
# For numeric values, present is defined as non-zero.
|
141
145
|
#
|
142
146
|
# For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
|
143
147
|
# to determine whether the user has a name:
|
@@ -217,25 +221,9 @@ module ActiveRecord #:nodoc:
|
|
217
221
|
#
|
218
222
|
# == Single table inheritance
|
219
223
|
#
|
220
|
-
# Active Record allows inheritance by storing the name of the class in a
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
# class Company < ActiveRecord::Base; end
|
225
|
-
# class Firm < Company; end
|
226
|
-
# class Client < Company; end
|
227
|
-
# class PriorityClient < Client; end
|
228
|
-
#
|
229
|
-
# When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in
|
230
|
-
# the companies table with type = "Firm". You can then fetch this row again using
|
231
|
-
# <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.
|
232
|
-
#
|
233
|
-
# If you don't have a type column defined in your table, single-table inheritance won't
|
234
|
-
# be triggered. In that case, it'll work just like normal subclasses with no special magic
|
235
|
-
# for differentiating between them or reloading the right type with find.
|
236
|
-
#
|
237
|
-
# Note, all the attributes for all the cases are kept in the same table. Read more:
|
238
|
-
# http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
|
224
|
+
# Active Record allows inheritance by storing the name of the class in a
|
225
|
+
# column that is named "type" by default. See ActiveRecord::Inheritance for
|
226
|
+
# more details.
|
239
227
|
#
|
240
228
|
# == Connection to multiple databases in different models
|
241
229
|
#
|
@@ -306,6 +294,8 @@ module ActiveRecord #:nodoc:
|
|
306
294
|
include Integration
|
307
295
|
include Validations
|
308
296
|
include CounterCache
|
297
|
+
include Attributes
|
298
|
+
include AttributeDecorators
|
309
299
|
include Locking::Optimistic
|
310
300
|
include Locking::Pessimistic
|
311
301
|
include AttributeMethods
|