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.

Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -2176
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/association_scope.rb +53 -21
  7. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  8. data/lib/active_record/associations/builder/association.rb +16 -5
  9. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  10. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  11. data/lib/active_record/associations/builder/has_one.rb +2 -2
  12. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  13. data/lib/active_record/associations/collection_association.rb +32 -44
  14. data/lib/active_record/associations/collection_proxy.rb +1 -10
  15. data/lib/active_record/associations/has_many_association.rb +60 -14
  16. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  17. data/lib/active_record/associations/has_one_association.rb +0 -1
  18. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  19. data/lib/active_record/associations/join_dependency.rb +7 -9
  20. data/lib/active_record/associations/preloader/association.rb +9 -5
  21. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/singular_association.rb +16 -1
  24. data/lib/active_record/associations/through_association.rb +6 -22
  25. data/lib/active_record/associations.rb +58 -33
  26. data/lib/active_record/attribute.rb +131 -0
  27. data/lib/active_record/attribute_assignment.rb +19 -11
  28. data/lib/active_record/attribute_decorators.rb +66 -0
  29. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  30. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  31. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  32. data/lib/active_record/attribute_methods/read.rb +14 -57
  33. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  34. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  35. data/lib/active_record/attribute_methods/write.rb +8 -23
  36. data/lib/active_record/attribute_methods.rb +53 -90
  37. data/lib/active_record/attribute_set/builder.rb +32 -0
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attributes.rb +122 -0
  40. data/lib/active_record/autosave_association.rb +11 -21
  41. data/lib/active_record/base.rb +9 -19
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  52. data/lib/active_record/connection_adapters/column.rb +13 -244
  53. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  55. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  56. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  57. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  58. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  59. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  60. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  61. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  85. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  86. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  87. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  88. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  89. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  90. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  91. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  93. data/lib/active_record/connection_handling.rb +1 -1
  94. data/lib/active_record/core.rb +119 -22
  95. data/lib/active_record/counter_cache.rb +60 -6
  96. data/lib/active_record/enum.rb +9 -10
  97. data/lib/active_record/errors.rb +27 -26
  98. data/lib/active_record/explain.rb +1 -1
  99. data/lib/active_record/fixtures.rb +52 -45
  100. data/lib/active_record/gem_version.rb +3 -3
  101. data/lib/active_record/inheritance.rb +33 -8
  102. data/lib/active_record/integration.rb +4 -4
  103. data/lib/active_record/locking/optimistic.rb +34 -16
  104. data/lib/active_record/migration/command_recorder.rb +19 -2
  105. data/lib/active_record/migration/join_table.rb +1 -1
  106. data/lib/active_record/migration.rb +22 -32
  107. data/lib/active_record/model_schema.rb +39 -48
  108. data/lib/active_record/nested_attributes.rb +8 -18
  109. data/lib/active_record/persistence.rb +39 -22
  110. data/lib/active_record/query_cache.rb +3 -3
  111. data/lib/active_record/querying.rb +1 -8
  112. data/lib/active_record/railtie.rb +17 -10
  113. data/lib/active_record/railties/databases.rake +47 -42
  114. data/lib/active_record/readonly_attributes.rb +0 -1
  115. data/lib/active_record/reflection.rb +225 -92
  116. data/lib/active_record/relation/batches.rb +0 -2
  117. data/lib/active_record/relation/calculations.rb +28 -32
  118. data/lib/active_record/relation/delegation.rb +1 -1
  119. data/lib/active_record/relation/finder_methods.rb +42 -20
  120. data/lib/active_record/relation/merger.rb +0 -1
  121. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  122. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/query_methods.rb +98 -62
  125. data/lib/active_record/relation/spawn_methods.rb +6 -7
  126. data/lib/active_record/relation.rb +35 -11
  127. data/lib/active_record/result.rb +16 -9
  128. data/lib/active_record/sanitization.rb +8 -1
  129. data/lib/active_record/schema.rb +0 -1
  130. data/lib/active_record/schema_dumper.rb +51 -9
  131. data/lib/active_record/schema_migration.rb +4 -0
  132. data/lib/active_record/scoping/default.rb +5 -4
  133. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  134. data/lib/active_record/statement_cache.rb +79 -5
  135. data/lib/active_record/store.rb +5 -5
  136. data/lib/active_record/tasks/database_tasks.rb +37 -5
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  138. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  139. data/lib/active_record/timestamp.rb +9 -7
  140. data/lib/active_record/transactions.rb +35 -21
  141. data/lib/active_record/type/binary.rb +40 -0
  142. data/lib/active_record/type/boolean.rb +19 -0
  143. data/lib/active_record/type/date.rb +46 -0
  144. data/lib/active_record/type/date_time.rb +43 -0
  145. data/lib/active_record/type/decimal.rb +40 -0
  146. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  147. data/lib/active_record/type/float.rb +19 -0
  148. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  149. data/lib/active_record/type/integer.rb +23 -0
  150. data/lib/active_record/type/mutable.rb +16 -0
  151. data/lib/active_record/type/numeric.rb +36 -0
  152. data/lib/active_record/type/serialized.rb +51 -0
  153. data/lib/active_record/type/string.rb +36 -0
  154. data/lib/active_record/type/text.rb +11 -0
  155. data/lib/active_record/type/time.rb +26 -0
  156. data/lib/active_record/type/time_value.rb +38 -0
  157. data/lib/active_record/type/type_map.rb +48 -0
  158. data/lib/active_record/type/value.rb +101 -0
  159. data/lib/active_record/type.rb +20 -0
  160. data/lib/active_record/validations/uniqueness.rb +9 -23
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record.rb +2 -1
  163. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  164. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  165. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  166. metadata +71 -14
  167. 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 | X | X | X
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 | X | X | X
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.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
540
- # @firm.invoices # selects all invoices by going through the Client join model
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+N problem in which fetching 100
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 101 queries can be reduced to 2.
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('LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = true')
788
+ # Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'")
781
789
  #
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
- #
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("deleted = 0").order("name") }, class_name: "Person"
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 now)
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::AssociationReflection.new(:has_and_belongs_to_many, name, scope, options, self)
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.to_sym, habtm_reflection]
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.to_sym].parent_reflection = [name.to_sym, habtm_reflection]
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, NameError
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, :column
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
- @column = object.class.reflect_on_aggregation(name.to_sym) || object.column_for_attribute(name)
121
- klass = column.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(klass)
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, column)
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 :timestamp) there is no need to validate if
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 column.type == :time
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(klass)
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
- set_values = values.values_at(*positions)
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