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
@@ -202,12 +202,13 @@ module ActiveRecord
|
|
202
202
|
# For instance, +attributes+ and +connection+ would be bad choices for association names.
|
203
203
|
#
|
204
204
|
# == Auto-generated methods
|
205
|
+
# See also Instance Public methods below for more details.
|
205
206
|
#
|
206
207
|
# === Singular associations (one-to-one)
|
207
208
|
# | | belongs_to |
|
208
209
|
# generated methods | belongs_to | :polymorphic | has_one
|
209
210
|
# ----------------------------------+------------+--------------+---------
|
210
|
-
# other
|
211
|
+
# other(force_reload=false) | X | X | X
|
211
212
|
# other=(other) | X | X | X
|
212
213
|
# build_other(attributes={}) | X | | X
|
213
214
|
# create_other(attributes={}) | X | | X
|
@@ -217,7 +218,7 @@ module ActiveRecord
|
|
217
218
|
# | | | has_many
|
218
219
|
# generated methods | habtm | has_many | :through
|
219
220
|
# ----------------------------------+-------+----------+----------
|
220
|
-
# others
|
221
|
+
# others(force_reload=false) | X | X | X
|
221
222
|
# others=(other,other,...) | X | X | X
|
222
223
|
# other_ids | X | X | X
|
223
224
|
# other_ids=(id,id,...) | X | X | X
|
@@ -419,6 +420,10 @@ module ActiveRecord
|
|
419
420
|
# has_many :birthday_events, ->(user) { where starts_on: user.birthday }, class_name: 'Event'
|
420
421
|
# end
|
421
422
|
#
|
423
|
+
# Note: Joining, eager loading and preloading of these associations is not fully possible.
|
424
|
+
# These operations happen before instance creation and the scope will be called with a +nil+ argument.
|
425
|
+
# This can lead to unexpected behavior and is deprecated.
|
426
|
+
#
|
422
427
|
# == Association callbacks
|
423
428
|
#
|
424
429
|
# Similar to the normal callbacks that hook into the life cycle of an Active Record object,
|
@@ -536,8 +541,8 @@ module ActiveRecord
|
|
536
541
|
# end
|
537
542
|
#
|
538
543
|
# @firm = Firm.first
|
539
|
-
# @firm.clients.
|
540
|
-
# @firm.invoices
|
544
|
+
# @firm.clients.flat_map { |c| c.invoices } # select all invoices for all clients of the firm
|
545
|
+
# @firm.invoices # selects all invoices by going through the Client join model
|
541
546
|
#
|
542
547
|
# Similarly you can go through a +has_one+ association on the join model:
|
543
548
|
#
|
@@ -712,9 +717,9 @@ module ActiveRecord
|
|
712
717
|
# == Eager loading of associations
|
713
718
|
#
|
714
719
|
# Eager loading is a way to find objects of a certain class and a number of named associations.
|
715
|
-
# This is one of the easiest ways of to prevent the dreaded 1
|
720
|
+
# This is one of the easiest ways of to prevent the dreaded N+1 problem in which fetching 100
|
716
721
|
# posts that each need to display their author triggers 101 database queries. Through the
|
717
|
-
# use of eager loading, the
|
722
|
+
# use of eager loading, the number of queries will be reduced from 101 to 2.
|
718
723
|
#
|
719
724
|
# class Post < ActiveRecord::Base
|
720
725
|
# belongs_to :author
|
@@ -774,16 +779,15 @@ module ActiveRecord
|
|
774
779
|
# In the above example posts with no approved comments are not returned at all, because
|
775
780
|
# the conditions apply to the SQL statement as a whole and not just to the association.
|
776
781
|
#
|
782
|
+
# You must disambiguate column references for this fallback to happen, for example
|
783
|
+
# <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.
|
784
|
+
#
|
777
785
|
# If you want to load all posts (including posts with no approved comments) then write
|
778
786
|
# your own LEFT OUTER JOIN query using ON
|
779
787
|
#
|
780
|
-
# Post.joins(
|
788
|
+
# Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'")
|
781
789
|
#
|
782
|
-
#
|
783
|
-
# <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.
|
784
|
-
#
|
785
|
-
# If you do want eager load only some members of an association it is usually more natural
|
786
|
-
# to include an association which has conditions defined on it:
|
790
|
+
# In this case it is usually more natural to include an association which has conditions defined on it:
|
787
791
|
#
|
788
792
|
# class Post < ActiveRecord::Base
|
789
793
|
# has_many :approved_comments, -> { where approved: true }, class_name: 'Comment'
|
@@ -1048,6 +1052,9 @@ module ActiveRecord
|
|
1048
1052
|
# Specifies a one-to-many association. The following methods for retrieval and query of
|
1049
1053
|
# collections of associated objects will be added:
|
1050
1054
|
#
|
1055
|
+
# +collection+ is a placeholder for the symbol passed as the +name+ argument, so
|
1056
|
+
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
|
1057
|
+
#
|
1051
1058
|
# [collection(force_reload = false)]
|
1052
1059
|
# Returns an array of all the associated objects.
|
1053
1060
|
# An empty array is returned if none are found.
|
@@ -1106,9 +1113,6 @@ module ActiveRecord
|
|
1106
1113
|
# Does the same as <tt>collection.create</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
|
1107
1114
|
# if the record is invalid.
|
1108
1115
|
#
|
1109
|
-
# (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
|
1110
|
-
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
|
1111
|
-
#
|
1112
1116
|
# === Example
|
1113
1117
|
#
|
1114
1118
|
# A <tt>Firm</tt> class declares <tt>has_many :clients</tt>, which will add:
|
@@ -1127,7 +1131,7 @@ module ActiveRecord
|
|
1127
1131
|
# * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
|
1128
1132
|
# * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
|
1129
1133
|
# * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save!</tt>)
|
1130
|
-
# The declaration can also include an options hash to specialize the behavior of the association.
|
1134
|
+
# The declaration can also include an +options+ hash to specialize the behavior of the association.
|
1131
1135
|
#
|
1132
1136
|
# === Options
|
1133
1137
|
# [:class_name]
|
@@ -1205,7 +1209,7 @@ module ActiveRecord
|
|
1205
1209
|
# Option examples:
|
1206
1210
|
# has_many :comments, -> { order "posted_on" }
|
1207
1211
|
# has_many :comments, -> { includes :author }
|
1208
|
-
# has_many :people, -> { where(
|
1212
|
+
# has_many :people, -> { where(deleted: false).order("name") }, class_name: "Person"
|
1209
1213
|
# has_many :tracks, -> { order "position" }, dependent: :destroy
|
1210
1214
|
# has_many :comments, dependent: :nullify
|
1211
1215
|
# has_many :tags, as: :taggable
|
@@ -1223,6 +1227,9 @@ module ActiveRecord
|
|
1223
1227
|
#
|
1224
1228
|
# The following methods for retrieval and query of a single associated object will be added:
|
1225
1229
|
#
|
1230
|
+
# +association+ is a placeholder for the symbol passed as the +name+ argument, so
|
1231
|
+
# <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.
|
1232
|
+
#
|
1226
1233
|
# [association(force_reload = false)]
|
1227
1234
|
# Returns the associated object. +nil+ is returned if none is found.
|
1228
1235
|
# [association=(associate)]
|
@@ -1241,9 +1248,6 @@ module ActiveRecord
|
|
1241
1248
|
# Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
|
1242
1249
|
# if the record is invalid.
|
1243
1250
|
#
|
1244
|
-
# (+association+ is replaced with the symbol passed as the first argument, so
|
1245
|
-
# <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
|
1246
|
-
#
|
1247
1251
|
# === Example
|
1248
1252
|
#
|
1249
1253
|
# An Account class declares <tt>has_one :beneficiary</tt>, which will add:
|
@@ -1255,7 +1259,7 @@ module ActiveRecord
|
|
1255
1259
|
#
|
1256
1260
|
# === Options
|
1257
1261
|
#
|
1258
|
-
# The declaration can also include an options hash to specialize the behavior of the association.
|
1262
|
+
# The declaration can also include an +options+ hash to specialize the behavior of the association.
|
1259
1263
|
#
|
1260
1264
|
# Options are:
|
1261
1265
|
# [:class_name]
|
@@ -1305,6 +1309,10 @@ module ActiveRecord
|
|
1305
1309
|
# that is the inverse of this <tt>has_one</tt> association. Does not work in combination
|
1306
1310
|
# with <tt>:through</tt> or <tt>:as</tt> options.
|
1307
1311
|
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
|
1312
|
+
# [:required]
|
1313
|
+
# When set to +true+, the association will also have its presence validated.
|
1314
|
+
# This will validate the association itself, not the id. You can use
|
1315
|
+
# +:inverse_of+ to avoid an extra query during validation.
|
1308
1316
|
#
|
1309
1317
|
# Option examples:
|
1310
1318
|
# has_one :credit_card, dependent: :destroy # destroys the associated credit card
|
@@ -1316,6 +1324,7 @@ module ActiveRecord
|
|
1316
1324
|
# has_one :boss, readonly: :true
|
1317
1325
|
# has_one :club, through: :membership
|
1318
1326
|
# has_one :primary_address, -> { where primary: true }, through: :addressables, source: :addressable
|
1327
|
+
# has_one :credit_card, required: true
|
1319
1328
|
def has_one(name, scope = nil, options = {})
|
1320
1329
|
reflection = Builder::HasOne.build(self, name, scope, options)
|
1321
1330
|
Reflection.add_reflection self, name, reflection
|
@@ -1329,6 +1338,9 @@ module ActiveRecord
|
|
1329
1338
|
# Methods will be added for retrieval and query for a single associated object, for which
|
1330
1339
|
# this object holds an id:
|
1331
1340
|
#
|
1341
|
+
# +association+ is a placeholder for the symbol passed as the +name+ argument, so
|
1342
|
+
# <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
|
1343
|
+
#
|
1332
1344
|
# [association(force_reload = false)]
|
1333
1345
|
# Returns the associated object. +nil+ is returned if none is found.
|
1334
1346
|
# [association=(associate)]
|
@@ -1344,9 +1356,6 @@ module ActiveRecord
|
|
1344
1356
|
# Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
|
1345
1357
|
# if the record is invalid.
|
1346
1358
|
#
|
1347
|
-
# (+association+ is replaced with the symbol passed as the first argument, so
|
1348
|
-
# <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
|
1349
|
-
#
|
1350
1359
|
# === Example
|
1351
1360
|
#
|
1352
1361
|
# A Post class declares <tt>belongs_to :author</tt>, which will add:
|
@@ -1355,7 +1364,18 @@ module ActiveRecord
|
|
1355
1364
|
# * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
|
1356
1365
|
# * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
|
1357
1366
|
# * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
|
1358
|
-
# The declaration can also include an options hash to specialize the behavior of the association.
|
1367
|
+
# The declaration can also include an +options+ hash to specialize the behavior of the association.
|
1368
|
+
#
|
1369
|
+
# === Scopes
|
1370
|
+
#
|
1371
|
+
# You can pass a second argument +scope+ as a callable (i.e. proc or
|
1372
|
+
# lambda) to retrieve a specific record or customize the generated query
|
1373
|
+
# when you access the associated object.
|
1374
|
+
#
|
1375
|
+
# Scope examples:
|
1376
|
+
# belongs_to :user, -> { where(id: 2) }
|
1377
|
+
# belongs_to :user, -> { joins(:friends) }
|
1378
|
+
# belongs_to :level, ->(level) { where("game_level > ?", level.current) }
|
1359
1379
|
#
|
1360
1380
|
# === Options
|
1361
1381
|
#
|
@@ -1409,7 +1429,7 @@ module ActiveRecord
|
|
1409
1429
|
#
|
1410
1430
|
# Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
|
1411
1431
|
# [:touch]
|
1412
|
-
# If true, the associated object will be touched (the updated_at/on attributes set to
|
1432
|
+
# If true, the associated object will be touched (the updated_at/on attributes set to current time)
|
1413
1433
|
# when this record is either saved or destroyed. If you specify a symbol, that attribute
|
1414
1434
|
# will be updated with the current time in addition to the updated_at/on attribute.
|
1415
1435
|
# [:inverse_of]
|
@@ -1417,6 +1437,10 @@ module ActiveRecord
|
|
1417
1437
|
# object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
|
1418
1438
|
# combination with the <tt>:polymorphic</tt> options.
|
1419
1439
|
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
|
1440
|
+
# [:required]
|
1441
|
+
# When set to +true+, the association will also have its presence validated.
|
1442
|
+
# This will validate the association itself, not the id. You can use
|
1443
|
+
# +:inverse_of+ to avoid an extra query during validation.
|
1420
1444
|
#
|
1421
1445
|
# Option examples:
|
1422
1446
|
# belongs_to :firm, foreign_key: "client_of"
|
@@ -1429,6 +1453,7 @@ module ActiveRecord
|
|
1429
1453
|
# belongs_to :post, counter_cache: true
|
1430
1454
|
# belongs_to :company, touch: true
|
1431
1455
|
# belongs_to :company, touch: :employees_last_updated_at
|
1456
|
+
# belongs_to :company, required: true
|
1432
1457
|
def belongs_to(name, scope = nil, options = {})
|
1433
1458
|
reflection = Builder::BelongsTo.build(self, name, scope, options)
|
1434
1459
|
Reflection.add_reflection self, name, reflection
|
@@ -1466,6 +1491,9 @@ module ActiveRecord
|
|
1466
1491
|
#
|
1467
1492
|
# Adds the following methods for retrieval and query:
|
1468
1493
|
#
|
1494
|
+
# +collection+ is a placeholder for the symbol passed as the +name+ argument, so
|
1495
|
+
# <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
|
1496
|
+
#
|
1469
1497
|
# [collection(force_reload = false)]
|
1470
1498
|
# Returns an array of all the associated objects.
|
1471
1499
|
# An empty array is returned if none are found.
|
@@ -1507,9 +1535,6 @@ module ActiveRecord
|
|
1507
1535
|
# with +attributes+, linked to this object through the join table, and that has already been
|
1508
1536
|
# saved (if it passed the validation).
|
1509
1537
|
#
|
1510
|
-
# (+collection+ is replaced with the symbol passed as the first argument, so
|
1511
|
-
# <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
|
1512
|
-
#
|
1513
1538
|
# === Example
|
1514
1539
|
#
|
1515
1540
|
# A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
|
@@ -1527,7 +1552,7 @@ module ActiveRecord
|
|
1527
1552
|
# * <tt>Developer#projects.exists?(...)</tt>
|
1528
1553
|
# * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
|
1529
1554
|
# * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
|
1530
|
-
# The declaration may include an options hash to specialize the behavior of the association.
|
1555
|
+
# The declaration may include an +options+ hash to specialize the behavior of the association.
|
1531
1556
|
#
|
1532
1557
|
# === Options
|
1533
1558
|
#
|
@@ -1573,7 +1598,7 @@ module ActiveRecord
|
|
1573
1598
|
scope = nil
|
1574
1599
|
end
|
1575
1600
|
|
1576
|
-
habtm_reflection = ActiveRecord::Reflection::
|
1601
|
+
habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
|
1577
1602
|
|
1578
1603
|
builder = Builder::HasAndBelongsToMany.new name, self, options
|
1579
1604
|
|
@@ -1588,7 +1613,7 @@ module ActiveRecord
|
|
1588
1613
|
|
1589
1614
|
Builder::HasMany.define_callbacks self, middle_reflection
|
1590
1615
|
Reflection.add_reflection self, middle_reflection.name, middle_reflection
|
1591
|
-
middle_reflection.parent_reflection = [name.
|
1616
|
+
middle_reflection.parent_reflection = [name.to_s, habtm_reflection]
|
1592
1617
|
|
1593
1618
|
include Module.new {
|
1594
1619
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
@@ -1609,7 +1634,7 @@ module ActiveRecord
|
|
1609
1634
|
end
|
1610
1635
|
|
1611
1636
|
has_many name, scope, hm_options, &extension
|
1612
|
-
self._reflections[name.
|
1637
|
+
self._reflections[name.to_s].parent_reflection = [name.to_s, habtm_reflection]
|
1613
1638
|
end
|
1614
1639
|
end
|
1615
1640
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Attribute # :nodoc:
|
3
|
+
class << self
|
4
|
+
def from_database(name, value, type)
|
5
|
+
FromDatabase.new(name, value, type)
|
6
|
+
end
|
7
|
+
|
8
|
+
def from_user(name, value, type)
|
9
|
+
FromUser.new(name, value, type)
|
10
|
+
end
|
11
|
+
|
12
|
+
def null(name)
|
13
|
+
Null.new(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def uninitialized(name, type)
|
17
|
+
Uninitialized.new(name, type)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :name, :value_before_type_cast, :type
|
22
|
+
|
23
|
+
# This method should not be called directly.
|
24
|
+
# Use #from_database or #from_user
|
25
|
+
def initialize(name, value_before_type_cast, type)
|
26
|
+
@name = name
|
27
|
+
@value_before_type_cast = value_before_type_cast
|
28
|
+
@type = type
|
29
|
+
end
|
30
|
+
|
31
|
+
def value
|
32
|
+
# `defined?` is cheaper than `||=` when we get back falsy values
|
33
|
+
@value = original_value unless defined?(@value)
|
34
|
+
@value
|
35
|
+
end
|
36
|
+
|
37
|
+
def original_value
|
38
|
+
type_cast(value_before_type_cast)
|
39
|
+
end
|
40
|
+
|
41
|
+
def value_for_database
|
42
|
+
type.type_cast_for_database(value)
|
43
|
+
end
|
44
|
+
|
45
|
+
def changed_from?(old_value)
|
46
|
+
type.changed?(old_value, value, value_before_type_cast)
|
47
|
+
end
|
48
|
+
|
49
|
+
def changed_in_place_from?(old_value)
|
50
|
+
type.changed_in_place?(old_value, value)
|
51
|
+
end
|
52
|
+
|
53
|
+
def with_value_from_user(value)
|
54
|
+
self.class.from_user(name, value, type)
|
55
|
+
end
|
56
|
+
|
57
|
+
def with_value_from_database(value)
|
58
|
+
self.class.from_database(name, value, type)
|
59
|
+
end
|
60
|
+
|
61
|
+
def type_cast(*)
|
62
|
+
raise NotImplementedError
|
63
|
+
end
|
64
|
+
|
65
|
+
def initialized?
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def ==(other)
|
70
|
+
self.class == other.class &&
|
71
|
+
name == other.name &&
|
72
|
+
value_before_type_cast == other.value_before_type_cast &&
|
73
|
+
type == other.type
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def initialize_dup(other)
|
79
|
+
if defined?(@value) && @value.duplicable?
|
80
|
+
@value = @value.dup
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class FromDatabase < Attribute # :nodoc:
|
85
|
+
def type_cast(value)
|
86
|
+
type.type_cast_from_database(value)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class FromUser < Attribute # :nodoc:
|
91
|
+
def type_cast(value)
|
92
|
+
type.type_cast_from_user(value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Null < Attribute # :nodoc:
|
97
|
+
def initialize(name)
|
98
|
+
super(name, nil, Type::Value.new)
|
99
|
+
end
|
100
|
+
|
101
|
+
def value
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def with_value_from_database(value)
|
106
|
+
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
|
107
|
+
end
|
108
|
+
alias_method :with_value_from_user, :with_value_from_database
|
109
|
+
end
|
110
|
+
|
111
|
+
class Uninitialized < Attribute # :nodoc:
|
112
|
+
def initialize(name, type)
|
113
|
+
super(name, nil, type)
|
114
|
+
end
|
115
|
+
|
116
|
+
def value
|
117
|
+
if block_given?
|
118
|
+
yield name
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def value_for_database
|
123
|
+
end
|
124
|
+
|
125
|
+
def initialized?
|
126
|
+
false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
private_constant :FromDatabase, :FromUser, :Null, :Uninitialized
|
130
|
+
end
|
131
|
+
end
|
@@ -11,6 +11,15 @@ module ActiveRecord
|
|
11
11
|
# If the passed hash responds to <tt>permitted?</tt> method and the return value
|
12
12
|
# of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt>
|
13
13
|
# exception is raised.
|
14
|
+
#
|
15
|
+
# cat = Cat.new(name: "Gorby", status: "yawning")
|
16
|
+
# cat.attributes # => { "name" => "Gorby", "status" => "yawning", "created_at" => nil, "updated_at" => nil}
|
17
|
+
# cat.assign_attributes(status: "sleeping")
|
18
|
+
# cat.attributes # => { "name" => "Gorby", "status" => "sleeping", "created_at" => nil, "updated_at" => nil }
|
19
|
+
#
|
20
|
+
# New attributes will be persisted in the database when the object is saved.
|
21
|
+
#
|
22
|
+
# Aliased to <tt>attributes=</tt>.
|
14
23
|
def assign_attributes(new_attributes)
|
15
24
|
if !new_attributes.respond_to?(:stringify_keys)
|
16
25
|
raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
|
@@ -43,7 +52,7 @@ module ActiveRecord
|
|
43
52
|
|
44
53
|
def _assign_attribute(k, v)
|
45
54
|
public_send("#{k}=", v)
|
46
|
-
rescue NoMethodError
|
55
|
+
rescue NoMethodError
|
47
56
|
if respond_to?("#{k}=")
|
48
57
|
raise
|
49
58
|
else
|
@@ -106,7 +115,7 @@ module ActiveRecord
|
|
106
115
|
end
|
107
116
|
|
108
117
|
class MultiparameterAttribute #:nodoc:
|
109
|
-
attr_reader :object, :name, :values, :
|
118
|
+
attr_reader :object, :name, :values, :cast_type
|
110
119
|
|
111
120
|
def initialize(object, name, values)
|
112
121
|
@object = object
|
@@ -117,22 +126,22 @@ module ActiveRecord
|
|
117
126
|
def read_value
|
118
127
|
return if values.values.compact.empty?
|
119
128
|
|
120
|
-
@
|
121
|
-
klass
|
129
|
+
@cast_type = object.type_for_attribute(name)
|
130
|
+
klass = cast_type.klass
|
122
131
|
|
123
132
|
if klass == Time
|
124
133
|
read_time
|
125
134
|
elsif klass == Date
|
126
135
|
read_date
|
127
136
|
else
|
128
|
-
read_other
|
137
|
+
read_other
|
129
138
|
end
|
130
139
|
end
|
131
140
|
|
132
141
|
private
|
133
142
|
|
134
143
|
def instantiate_time_object(set_values)
|
135
|
-
if object.class.send(:create_time_zone_conversion_attribute?, name,
|
144
|
+
if object.class.send(:create_time_zone_conversion_attribute?, name, cast_type)
|
136
145
|
Time.zone.local(*set_values)
|
137
146
|
else
|
138
147
|
Time.send(object.class.default_timezone, *set_values)
|
@@ -140,9 +149,9 @@ module ActiveRecord
|
|
140
149
|
end
|
141
150
|
|
142
151
|
def read_time
|
143
|
-
# If column is a :time (and not :date or :
|
152
|
+
# If column is a :time (and not :date or :datetime) there is no need to validate if
|
144
153
|
# there are year/month/day fields
|
145
|
-
if
|
154
|
+
if cast_type.type == :time
|
146
155
|
# if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
|
147
156
|
{ 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value|
|
148
157
|
values[key] ||= value
|
@@ -172,13 +181,12 @@ module ActiveRecord
|
|
172
181
|
end
|
173
182
|
end
|
174
183
|
|
175
|
-
def read_other
|
184
|
+
def read_other
|
176
185
|
max_position = extract_max_param
|
177
186
|
positions = (1..max_position)
|
178
187
|
validate_required_parameters!(positions)
|
179
188
|
|
180
|
-
|
181
|
-
klass.new(*set_values)
|
189
|
+
values.slice(*positions)
|
182
190
|
end
|
183
191
|
|
184
192
|
# Checks whether some blank date parameter exists. Note that this is different
|
@@ -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
|
@@ -43,7 +43,7 @@ module ActiveRecord
|
|
43
43
|
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
44
44
|
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
|
45
45
|
def read_attribute_before_type_cast(attr_name)
|
46
|
-
@attributes[attr_name.to_s]
|
46
|
+
@attributes[attr_name.to_s].value_before_type_cast
|
47
47
|
end
|
48
48
|
|
49
49
|
# Returns a hash of attributes before typecasting and deserialization.
|
@@ -57,7 +57,7 @@ module ActiveRecord
|
|
57
57
|
# task.attributes_before_type_cast
|
58
58
|
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
|
59
59
|
def attributes_before_type_cast
|
60
|
-
@attributes
|
60
|
+
@attributes.values_before_type_cast
|
61
61
|
end
|
62
62
|
|
63
63
|
private
|