activerecord 4.1.0 → 4.2.0

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +776 -1330
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/association_relation.rb +4 -0
  6. data/lib/active_record/associations/alias_tracker.rb +14 -13
  7. data/lib/active_record/associations/association.rb +2 -2
  8. data/lib/active_record/associations/association_scope.rb +83 -43
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
  13. data/lib/active_record/associations/builder/has_many.rb +1 -1
  14. data/lib/active_record/associations/builder/has_one.rb +2 -2
  15. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  16. data/lib/active_record/associations/collection_association.rb +66 -29
  17. data/lib/active_record/associations/collection_proxy.rb +22 -26
  18. data/lib/active_record/associations/has_many_association.rb +65 -18
  19. data/lib/active_record/associations/has_many_through_association.rb +55 -27
  20. data/lib/active_record/associations/has_one_association.rb +0 -1
  21. data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
  22. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  23. data/lib/active_record/associations/join_dependency.rb +20 -12
  24. data/lib/active_record/associations/preloader/association.rb +34 -11
  25. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  26. data/lib/active_record/associations/preloader.rb +49 -59
  27. data/lib/active_record/associations/singular_association.rb +25 -4
  28. data/lib/active_record/associations/through_association.rb +23 -14
  29. data/lib/active_record/associations.rb +171 -42
  30. data/lib/active_record/attribute.rb +149 -0
  31. data/lib/active_record/attribute_assignment.rb +18 -10
  32. data/lib/active_record/attribute_decorators.rb +66 -0
  33. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  34. data/lib/active_record/attribute_methods/dirty.rb +98 -44
  35. data/lib/active_record/attribute_methods/primary_key.rb +14 -8
  36. data/lib/active_record/attribute_methods/query.rb +1 -1
  37. data/lib/active_record/attribute_methods/read.rb +22 -59
  38. data/lib/active_record/attribute_methods/serialization.rb +37 -147
  39. data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
  40. data/lib/active_record/attribute_methods/write.rb +14 -21
  41. data/lib/active_record/attribute_methods.rb +67 -94
  42. data/lib/active_record/attribute_set/builder.rb +86 -0
  43. data/lib/active_record/attribute_set.rb +77 -0
  44. data/lib/active_record/attributes.rb +139 -0
  45. data/lib/active_record/autosave_association.rb +45 -38
  46. data/lib/active_record/base.rb +10 -20
  47. data/lib/active_record/callbacks.rb +7 -7
  48. data/lib/active_record/coders/json.rb +13 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
  60. data/lib/active_record/connection_adapters/column.rb +28 -239
  61. data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
  62. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
  63. data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
  64. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  65. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  66. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
  67. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  96. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
  97. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  98. data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
  99. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  100. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
  101. data/lib/active_record/connection_handling.rb +3 -3
  102. data/lib/active_record/core.rb +143 -32
  103. data/lib/active_record/counter_cache.rb +60 -7
  104. data/lib/active_record/enum.rb +10 -11
  105. data/lib/active_record/errors.rb +49 -27
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixtures.rb +56 -70
  108. data/lib/active_record/gem_version.rb +2 -2
  109. data/lib/active_record/inheritance.rb +35 -10
  110. data/lib/active_record/integration.rb +4 -4
  111. data/lib/active_record/locking/optimistic.rb +35 -17
  112. data/lib/active_record/log_subscriber.rb +1 -1
  113. data/lib/active_record/migration/command_recorder.rb +19 -2
  114. data/lib/active_record/migration/join_table.rb +1 -1
  115. data/lib/active_record/migration.rb +52 -49
  116. data/lib/active_record/model_schema.rb +49 -57
  117. data/lib/active_record/nested_attributes.rb +7 -7
  118. data/lib/active_record/null_relation.rb +19 -5
  119. data/lib/active_record/persistence.rb +50 -31
  120. data/lib/active_record/query_cache.rb +3 -3
  121. data/lib/active_record/querying.rb +10 -7
  122. data/lib/active_record/railtie.rb +14 -11
  123. data/lib/active_record/railties/databases.rake +56 -54
  124. data/lib/active_record/readonly_attributes.rb +0 -1
  125. data/lib/active_record/reflection.rb +286 -102
  126. data/lib/active_record/relation/batches.rb +0 -1
  127. data/lib/active_record/relation/calculations.rb +39 -31
  128. data/lib/active_record/relation/delegation.rb +2 -2
  129. data/lib/active_record/relation/finder_methods.rb +80 -36
  130. data/lib/active_record/relation/merger.rb +25 -30
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
  132. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  133. data/lib/active_record/relation/predicate_builder.rb +11 -10
  134. data/lib/active_record/relation/query_methods.rb +141 -55
  135. data/lib/active_record/relation/spawn_methods.rb +3 -0
  136. data/lib/active_record/relation.rb +69 -30
  137. data/lib/active_record/result.rb +18 -7
  138. data/lib/active_record/sanitization.rb +12 -2
  139. data/lib/active_record/schema.rb +0 -1
  140. data/lib/active_record/schema_dumper.rb +58 -26
  141. data/lib/active_record/schema_migration.rb +11 -0
  142. data/lib/active_record/scoping/default.rb +8 -7
  143. data/lib/active_record/scoping/named.rb +4 -0
  144. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  145. data/lib/active_record/statement_cache.rb +95 -10
  146. data/lib/active_record/store.rb +19 -10
  147. data/lib/active_record/tasks/database_tasks.rb +73 -7
  148. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
  149. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  151. data/lib/active_record/timestamp.rb +11 -9
  152. data/lib/active_record/transactions.rb +37 -21
  153. data/lib/active_record/type/big_integer.rb +13 -0
  154. data/lib/active_record/type/binary.rb +50 -0
  155. data/lib/active_record/type/boolean.rb +30 -0
  156. data/lib/active_record/type/date.rb +46 -0
  157. data/lib/active_record/type/date_time.rb +43 -0
  158. data/lib/active_record/type/decimal.rb +40 -0
  159. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  160. data/lib/active_record/type/decorator.rb +14 -0
  161. data/lib/active_record/type/float.rb +19 -0
  162. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  163. data/lib/active_record/type/integer.rb +55 -0
  164. data/lib/active_record/type/mutable.rb +16 -0
  165. data/lib/active_record/type/numeric.rb +36 -0
  166. data/lib/active_record/type/serialized.rb +56 -0
  167. data/lib/active_record/type/string.rb +36 -0
  168. data/lib/active_record/type/text.rb +11 -0
  169. data/lib/active_record/type/time.rb +26 -0
  170. data/lib/active_record/type/time_value.rb +38 -0
  171. data/lib/active_record/type/type_map.rb +64 -0
  172. data/lib/active_record/type/unsigned_integer.rb +15 -0
  173. data/lib/active_record/type/value.rb +101 -0
  174. data/lib/active_record/type.rb +23 -0
  175. data/lib/active_record/validations/associated.rb +5 -3
  176. data/lib/active_record/validations/presence.rb +6 -4
  177. data/lib/active_record/validations/uniqueness.rb +11 -17
  178. data/lib/active_record/validations.rb +25 -19
  179. data/lib/active_record.rb +3 -0
  180. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  181. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
  182. data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +65 -10
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -4,6 +4,12 @@ require 'active_support/core_ext/module/remove_method'
4
4
  require 'active_record/errors'
5
5
 
6
6
  module ActiveRecord
7
+ class AssociationNotFoundError < ConfigurationError #:nodoc:
8
+ def initialize(record, association_name)
9
+ super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
10
+ end
11
+ end
12
+
7
13
  class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
8
14
  def initialize(reflection, associated_class = nil)
9
15
  super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
@@ -40,11 +46,17 @@ module ActiveRecord
40
46
  end
41
47
  end
42
48
 
49
+ class HasOneAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
50
+ def initialize(owner_class_name, reflection)
51
+ super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
52
+ end
53
+ end
54
+
43
55
  class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
44
56
  def initialize(reflection)
45
57
  through_reflection = reflection.through_reflection
46
58
  source_reflection_names = reflection.source_reflection_names
47
- source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
59
+ source_associations = reflection.through_reflection.klass._reflections.keys
48
60
  super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
49
61
  end
50
62
  end
@@ -145,7 +157,7 @@ module ActiveRecord
145
157
  association = association_instance_get(name)
146
158
 
147
159
  if association.nil?
148
- reflection = self.class.reflect_on_association(name)
160
+ raise AssociationNotFoundError.new(self, name) unless reflection = self.class._reflect_on_association(name)
149
161
  association = reflection.association_class.new(self, reflection)
150
162
  association_instance_set(name, association)
151
163
  end
@@ -196,12 +208,13 @@ module ActiveRecord
196
208
  # For instance, +attributes+ and +connection+ would be bad choices for association names.
197
209
  #
198
210
  # == Auto-generated methods
211
+ # See also Instance Public methods below for more details.
199
212
  #
200
213
  # === Singular associations (one-to-one)
201
214
  # | | belongs_to |
202
215
  # generated methods | belongs_to | :polymorphic | has_one
203
216
  # ----------------------------------+------------+--------------+---------
204
- # other | X | X | X
217
+ # other(force_reload=false) | X | X | X
205
218
  # other=(other) | X | X | X
206
219
  # build_other(attributes={}) | X | | X
207
220
  # create_other(attributes={}) | X | | X
@@ -211,7 +224,7 @@ module ActiveRecord
211
224
  # | | | has_many
212
225
  # generated methods | habtm | has_many | :through
213
226
  # ----------------------------------+-------+----------+----------
214
- # others | X | X | X
227
+ # others(force_reload=false) | X | X | X
215
228
  # others=(other,other,...) | X | X | X
216
229
  # other_ids | X | X | X
217
230
  # other_ids=(id,id,...) | X | X | X
@@ -413,6 +426,10 @@ module ActiveRecord
413
426
  # has_many :birthday_events, ->(user) { where starts_on: user.birthday }, class_name: 'Event'
414
427
  # end
415
428
  #
429
+ # Note: Joining, eager loading and preloading of these associations is not fully possible.
430
+ # These operations happen before instance creation and the scope will be called with a +nil+ argument.
431
+ # This can lead to unexpected behavior and is deprecated.
432
+ #
416
433
  # == Association callbacks
417
434
  #
418
435
  # Similar to the normal callbacks that hook into the life cycle of an Active Record object,
@@ -436,9 +453,11 @@ module ActiveRecord
436
453
  #
437
454
  # Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
438
455
  #
439
- # Should any of the +before_add+ callbacks throw an exception, the object does not get
440
- # added to the collection. Same with the +before_remove+ callbacks; if an exception is
441
- # thrown the object doesn't get removed.
456
+ # If any of the +before_add+ callbacks throw an exception, the object will not be
457
+ # added to the collection.
458
+ #
459
+ # Similarly, if any of the +before_remove+ callbacks throw an exception, the object
460
+ # will not be removed from the collection.
442
461
  #
443
462
  # == Association extensions
444
463
  #
@@ -530,8 +549,8 @@ module ActiveRecord
530
549
  # end
531
550
  #
532
551
  # @firm = Firm.first
533
- # @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
534
- # @firm.invoices # selects all invoices by going through the Client join model
552
+ # @firm.clients.flat_map { |c| c.invoices } # select all invoices for all clients of the firm
553
+ # @firm.invoices # selects all invoices by going through the Client join model
535
554
  #
536
555
  # Similarly you can go through a +has_one+ association on the join model:
537
556
  #
@@ -636,7 +655,7 @@ module ActiveRecord
636
655
  # belongs_to :commenter
637
656
  # end
638
657
  #
639
- # When using nested association, you will not be able to modify the association because there
658
+ # When using a nested association, you will not be able to modify the association because there
640
659
  # is not enough information to know what modification to make. For example, if you tried to
641
660
  # add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
642
661
  # intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
@@ -706,9 +725,9 @@ module ActiveRecord
706
725
  # == Eager loading of associations
707
726
  #
708
727
  # Eager loading is a way to find objects of a certain class and a number of named associations.
709
- # This is one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100
728
+ # It is one of the easiest ways to prevent the dreaded N+1 problem in which fetching 100
710
729
  # posts that each need to display their author triggers 101 database queries. Through the
711
- # use of eager loading, the 101 queries can be reduced to 2.
730
+ # use of eager loading, the number of queries will be reduced from 101 to 2.
712
731
  #
713
732
  # class Post < ActiveRecord::Base
714
733
  # belongs_to :author
@@ -738,16 +757,16 @@ module ActiveRecord
738
757
  # Post.includes(:author, :comments).each do |post|
739
758
  #
740
759
  # This will load all comments with a single query. This reduces the total number of queries
741
- # to 3. More generally the number of queries will be 1 plus the number of associations
760
+ # to 3. In general, the number of queries will be 1 plus the number of associations
742
761
  # named (except if some of the associations are polymorphic +belongs_to+ - see below).
743
762
  #
744
763
  # To include a deep hierarchy of associations, use a hash:
745
764
  #
746
- # Post.includes(:author, {comments: {author: :gravatar}}).each do |post|
765
+ # Post.includes(:author, { comments: { author: :gravatar } }).each do |post|
747
766
  #
748
- # That'll grab not only all the comments but all their authors and gravatar pictures.
749
- # You can mix and match symbols, arrays and hashes in any combination to describe the
750
- # associations you want to load.
767
+ # The above code will load all the comments and all of their associated
768
+ # authors and gravatars. You can mix and match any combination of symbols,
769
+ # arrays, and hashes to retrieve the associations you want to load.
751
770
  #
752
771
  # All of this power shouldn't fool you into thinking that you can pull out huge amounts
753
772
  # of data with no performance penalty just because you've reduced the number of queries.
@@ -756,8 +775,8 @@ module ActiveRecord
756
775
  # cut down on the number of queries in a situation as the one described above.
757
776
  #
758
777
  # Since only one table is loaded at a time, conditions or orders cannot reference tables
759
- # other than the main one. If this is the case Active Record falls back to the previously
760
- # used LEFT OUTER JOIN based strategy. For example
778
+ # other than the main one. If this is the case, Active Record falls back to the previously
779
+ # used LEFT OUTER JOIN based strategy. For example:
761
780
  #
762
781
  # Post.includes([:author, :comments]).where(['comments.approved = ?', true])
763
782
  #
@@ -767,11 +786,16 @@ module ActiveRecord
767
786
  # like this can have unintended consequences.
768
787
  # In the above example posts with no approved comments are not returned at all, because
769
788
  # the conditions apply to the SQL statement as a whole and not just to the association.
789
+ #
770
790
  # You must disambiguate column references for this fallback to happen, for example
771
791
  # <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.
772
792
  #
773
- # If you do want eager load only some members of an association it is usually more natural
774
- # to include an association which has conditions defined on it:
793
+ # If you want to load all posts (including posts with no approved comments) then write
794
+ # your own LEFT OUTER JOIN query using ON
795
+ #
796
+ # Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'")
797
+ #
798
+ # In this case it is usually more natural to include an association which has conditions defined on it:
775
799
  #
776
800
  # class Post < ActiveRecord::Base
777
801
  # has_many :approved_comments, -> { where approved: true }, class_name: 'Comment'
@@ -1036,6 +1060,9 @@ module ActiveRecord
1036
1060
  # Specifies a one-to-many association. The following methods for retrieval and query of
1037
1061
  # collections of associated objects will be added:
1038
1062
  #
1063
+ # +collection+ is a placeholder for the symbol passed as the +name+ argument, so
1064
+ # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.
1065
+ #
1039
1066
  # [collection(force_reload = false)]
1040
1067
  # Returns an array of all the associated objects.
1041
1068
  # An empty array is returned if none are found.
@@ -1094,9 +1121,6 @@ module ActiveRecord
1094
1121
  # Does the same as <tt>collection.create</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
1095
1122
  # if the record is invalid.
1096
1123
  #
1097
- # (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
1098
- # <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
1099
- #
1100
1124
  # === Example
1101
1125
  #
1102
1126
  # A <tt>Firm</tt> class declares <tt>has_many :clients</tt>, which will add:
@@ -1115,7 +1139,32 @@ module ActiveRecord
1115
1139
  # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
1116
1140
  # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
1117
1141
  # * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save!</tt>)
1118
- # The declaration can also include an options hash to specialize the behavior of the association.
1142
+ # The declaration can also include an +options+ hash to specialize the behavior of the association.
1143
+ #
1144
+ # === Scopes
1145
+ #
1146
+ # You can pass a second argument +scope+ as a callable (i.e. proc or
1147
+ # lambda) to retrieve a specific set of records or customize the generated
1148
+ # query when you access the associated collection.
1149
+ #
1150
+ # Scope examples:
1151
+ # has_many :comments, -> { where(author_id: 1) }
1152
+ # has_many :employees, -> { joins(:address) }
1153
+ # has_many :posts, ->(post) { where("max_post_length > ?", post.length) }
1154
+ #
1155
+ # === Extensions
1156
+ #
1157
+ # The +extension+ argument allows you to pass a block into a has_many
1158
+ # association. This is useful for adding new finders, creators and other
1159
+ # factory-type methods to be used as part of the association.
1160
+ #
1161
+ # Extension examples:
1162
+ # has_many :employees do
1163
+ # def find_or_create_by_name(name)
1164
+ # first_name, last_name = name.split(" ", 2)
1165
+ # find_or_create_by(first_name: first_name, last_name: last_name)
1166
+ # end
1167
+ # end
1119
1168
  #
1120
1169
  # === Options
1121
1170
  # [:class_name]
@@ -1127,8 +1176,14 @@ module ActiveRecord
1127
1176
  # Specify the foreign key used for the association. By default this is guessed to be the name
1128
1177
  # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+
1129
1178
  # association will use "person_id" as the default <tt>:foreign_key</tt>.
1179
+ # [:foreign_type]
1180
+ # Specify the column used to store the associated object's type, if this is a polymorphic
1181
+ # association. By default this is guessed to be the name of the polymorphic association
1182
+ # specified on "as" option with a "_type" suffix. So a class that defines a
1183
+ # <tt>has_many :tags, as: :taggable</tt> association will use "taggable_type" as the
1184
+ # default <tt>:foreign_type</tt>.
1130
1185
  # [:primary_key]
1131
- # Specify the method that returns the primary key used for the association. By default this is +id+.
1186
+ # Specify the name of the column to use as the primary key for the association. By default this is +id+.
1132
1187
  # [:dependent]
1133
1188
  # Controls what happens to the associated objects when
1134
1189
  # their owner is destroyed. Note that these are implemented as
@@ -1193,7 +1248,7 @@ module ActiveRecord
1193
1248
  # Option examples:
1194
1249
  # has_many :comments, -> { order "posted_on" }
1195
1250
  # has_many :comments, -> { includes :author }
1196
- # has_many :people, -> { where("deleted = 0").order("name") }, class_name: "Person"
1251
+ # has_many :people, -> { where(deleted: false).order("name") }, class_name: "Person"
1197
1252
  # has_many :tracks, -> { order "position" }, dependent: :destroy
1198
1253
  # has_many :comments, dependent: :nullify
1199
1254
  # has_many :tags, as: :taggable
@@ -1211,6 +1266,9 @@ module ActiveRecord
1211
1266
  #
1212
1267
  # The following methods for retrieval and query of a single associated object will be added:
1213
1268
  #
1269
+ # +association+ is a placeholder for the symbol passed as the +name+ argument, so
1270
+ # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.
1271
+ #
1214
1272
  # [association(force_reload = false)]
1215
1273
  # Returns the associated object. +nil+ is returned if none is found.
1216
1274
  # [association=(associate)]
@@ -1229,9 +1287,6 @@ module ActiveRecord
1229
1287
  # Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
1230
1288
  # if the record is invalid.
1231
1289
  #
1232
- # (+association+ is replaced with the symbol passed as the first argument, so
1233
- # <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
1234
- #
1235
1290
  # === Example
1236
1291
  #
1237
1292
  # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
@@ -1241,9 +1296,20 @@ module ActiveRecord
1241
1296
  # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
1242
1297
  # * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
1243
1298
  #
1299
+ # === Scopes
1300
+ #
1301
+ # You can pass a second argument +scope+ as a callable (i.e. proc or
1302
+ # lambda) to retrieve a specific record or customize the generated query
1303
+ # when you access the associated object.
1304
+ #
1305
+ # Scope examples:
1306
+ # has_one :author, -> { where(comment_id: 1) }
1307
+ # has_one :employer, -> { joins(:company) }
1308
+ # has_one :dob, ->(dob) { where("Date.new(2000, 01, 01) > ?", dob) }
1309
+ #
1244
1310
  # === Options
1245
1311
  #
1246
- # The declaration can also include an options hash to specialize the behavior of the association.
1312
+ # The declaration can also include an +options+ hash to specialize the behavior of the association.
1247
1313
  #
1248
1314
  # Options are:
1249
1315
  # [:class_name]
@@ -1263,6 +1329,12 @@ module ActiveRecord
1263
1329
  # Specify the foreign key used for the association. By default this is guessed to be the name
1264
1330
  # of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
1265
1331
  # will use "person_id" as the default <tt>:foreign_key</tt>.
1332
+ # [:foreign_type]
1333
+ # Specify the column used to store the associated object's type, if this is a polymorphic
1334
+ # association. By default this is guessed to be the name of the polymorphic association
1335
+ # specified on "as" option with a "_type" suffix. So a class that defines a
1336
+ # <tt>has_one :tag, as: :taggable</tt> association will use "taggable_type" as the
1337
+ # default <tt>:foreign_type</tt>.
1266
1338
  # [:primary_key]
1267
1339
  # Specify the method that returns the primary key used for the association. By default this is +id+.
1268
1340
  # [:as]
@@ -1293,6 +1365,10 @@ module ActiveRecord
1293
1365
  # that is the inverse of this <tt>has_one</tt> association. Does not work in combination
1294
1366
  # with <tt>:through</tt> or <tt>:as</tt> options.
1295
1367
  # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1368
+ # [:required]
1369
+ # When set to +true+, the association will also have its presence validated.
1370
+ # This will validate the association itself, not the id. You can use
1371
+ # +:inverse_of+ to avoid an extra query during validation.
1296
1372
  #
1297
1373
  # Option examples:
1298
1374
  # has_one :credit_card, dependent: :destroy # destroys the associated credit card
@@ -1304,6 +1380,7 @@ module ActiveRecord
1304
1380
  # has_one :boss, readonly: :true
1305
1381
  # has_one :club, through: :membership
1306
1382
  # has_one :primary_address, -> { where primary: true }, through: :addressables, source: :addressable
1383
+ # has_one :credit_card, required: true
1307
1384
  def has_one(name, scope = nil, options = {})
1308
1385
  reflection = Builder::HasOne.build(self, name, scope, options)
1309
1386
  Reflection.add_reflection self, name, reflection
@@ -1317,6 +1394,9 @@ module ActiveRecord
1317
1394
  # Methods will be added for retrieval and query for a single associated object, for which
1318
1395
  # this object holds an id:
1319
1396
  #
1397
+ # +association+ is a placeholder for the symbol passed as the +name+ argument, so
1398
+ # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.
1399
+ #
1320
1400
  # [association(force_reload = false)]
1321
1401
  # Returns the associated object. +nil+ is returned if none is found.
1322
1402
  # [association=(associate)]
@@ -1332,9 +1412,6 @@ module ActiveRecord
1332
1412
  # Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
1333
1413
  # if the record is invalid.
1334
1414
  #
1335
- # (+association+ is replaced with the symbol passed as the first argument, so
1336
- # <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
1337
- #
1338
1415
  # === Example
1339
1416
  #
1340
1417
  # A Post class declares <tt>belongs_to :author</tt>, which will add:
@@ -1343,7 +1420,18 @@ module ActiveRecord
1343
1420
  # * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
1344
1421
  # * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
1345
1422
  # * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
1346
- # The declaration can also include an options hash to specialize the behavior of the association.
1423
+ # The declaration can also include an +options+ hash to specialize the behavior of the association.
1424
+ #
1425
+ # === Scopes
1426
+ #
1427
+ # You can pass a second argument +scope+ as a callable (i.e. proc or
1428
+ # lambda) to retrieve a specific record or customize the generated query
1429
+ # when you access the associated object.
1430
+ #
1431
+ # Scope examples:
1432
+ # belongs_to :user, -> { where(id: 2) }
1433
+ # belongs_to :user, -> { joins(:friends) }
1434
+ # belongs_to :level, ->(level) { where("game_level > ?", level.current) }
1347
1435
  #
1348
1436
  # === Options
1349
1437
  #
@@ -1397,7 +1485,7 @@ module ActiveRecord
1397
1485
  #
1398
1486
  # Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
1399
1487
  # [:touch]
1400
- # If true, the associated object will be touched (the updated_at/on attributes set to now)
1488
+ # If true, the associated object will be touched (the updated_at/on attributes set to current time)
1401
1489
  # when this record is either saved or destroyed. If you specify a symbol, that attribute
1402
1490
  # will be updated with the current time in addition to the updated_at/on attribute.
1403
1491
  # [:inverse_of]
@@ -1405,18 +1493,23 @@ module ActiveRecord
1405
1493
  # object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
1406
1494
  # combination with the <tt>:polymorphic</tt> options.
1407
1495
  # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1496
+ # [:required]
1497
+ # When set to +true+, the association will also have its presence validated.
1498
+ # This will validate the association itself, not the id. You can use
1499
+ # +:inverse_of+ to avoid an extra query during validation.
1408
1500
  #
1409
1501
  # Option examples:
1410
1502
  # belongs_to :firm, foreign_key: "client_of"
1411
1503
  # belongs_to :person, primary_key: "name", foreign_key: "person_name"
1412
1504
  # belongs_to :author, class_name: "Person", foreign_key: "author_id"
1413
- # belongs_to :valid_coupon, ->(o) { where "discounts > #{o.payments_count}" },
1505
+ # belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count },
1414
1506
  # class_name: "Coupon", foreign_key: "coupon_id"
1415
1507
  # belongs_to :attachable, polymorphic: true
1416
1508
  # belongs_to :project, readonly: true
1417
1509
  # belongs_to :post, counter_cache: true
1418
1510
  # belongs_to :company, touch: true
1419
1511
  # belongs_to :company, touch: :employees_last_updated_at
1512
+ # belongs_to :company, required: true
1420
1513
  def belongs_to(name, scope = nil, options = {})
1421
1514
  reflection = Builder::BelongsTo.build(self, name, scope, options)
1422
1515
  Reflection.add_reflection self, name, reflection
@@ -1454,6 +1547,9 @@ module ActiveRecord
1454
1547
  #
1455
1548
  # Adds the following methods for retrieval and query:
1456
1549
  #
1550
+ # +collection+ is a placeholder for the symbol passed as the +name+ argument, so
1551
+ # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.
1552
+ #
1457
1553
  # [collection(force_reload = false)]
1458
1554
  # Returns an array of all the associated objects.
1459
1555
  # An empty array is returned if none are found.
@@ -1495,9 +1591,6 @@ module ActiveRecord
1495
1591
  # with +attributes+, linked to this object through the join table, and that has already been
1496
1592
  # saved (if it passed the validation).
1497
1593
  #
1498
- # (+collection+ is replaced with the symbol passed as the first argument, so
1499
- # <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
1500
- #
1501
1594
  # === Example
1502
1595
  #
1503
1596
  # A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
@@ -1515,7 +1608,34 @@ module ActiveRecord
1515
1608
  # * <tt>Developer#projects.exists?(...)</tt>
1516
1609
  # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
1517
1610
  # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
1518
- # The declaration may include an options hash to specialize the behavior of the association.
1611
+ # The declaration may include an +options+ hash to specialize the behavior of the association.
1612
+ #
1613
+ # === Scopes
1614
+ #
1615
+ # You can pass a second argument +scope+ as a callable (i.e. proc or
1616
+ # lambda) to retrieve a specific set of records or customize the generated
1617
+ # query when you access the associated collection.
1618
+ #
1619
+ # Scope examples:
1620
+ # has_and_belongs_to_many :projects, -> { includes :milestones, :manager }
1621
+ # has_and_belongs_to_many :categories, ->(category) {
1622
+ # where("default_category = ?", category.name)
1623
+ # }
1624
+ #
1625
+ # === Extensions
1626
+ #
1627
+ # The +extension+ argument allows you to pass a block into a
1628
+ # has_and_belongs_to_many association. This is useful for adding new
1629
+ # finders, creators and other factory-type methods to be used as part of
1630
+ # the association.
1631
+ #
1632
+ # Extension examples:
1633
+ # has_and_belongs_to_many :contractors do
1634
+ # def find_or_create_by_name(name)
1635
+ # first_name, last_name = name.split(" ", 2)
1636
+ # find_or_create_by(first_name: first_name, last_name: last_name)
1637
+ # end
1638
+ # end
1519
1639
  #
1520
1640
  # === Options
1521
1641
  #
@@ -1561,14 +1681,22 @@ module ActiveRecord
1561
1681
  scope = nil
1562
1682
  end
1563
1683
 
1684
+ habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(name, scope, options, self)
1685
+
1564
1686
  builder = Builder::HasAndBelongsToMany.new name, self, options
1565
1687
 
1566
1688
  join_model = builder.through_model
1567
1689
 
1690
+ # FIXME: we should move this to the internal constants. Also people
1691
+ # should never directly access this constant so I'm not happy about
1692
+ # setting it.
1693
+ const_set join_model.name, join_model
1694
+
1568
1695
  middle_reflection = builder.middle_reflection join_model
1569
1696
 
1570
1697
  Builder::HasMany.define_callbacks self, middle_reflection
1571
1698
  Reflection.add_reflection self, middle_reflection.name, middle_reflection
1699
+ middle_reflection.parent_reflection = [name.to_s, habtm_reflection]
1572
1700
 
1573
1701
  include Module.new {
1574
1702
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -1584,11 +1712,12 @@ module ActiveRecord
1584
1712
  hm_options[:through] = middle_reflection.name
1585
1713
  hm_options[:source] = join_model.right_reflection.name
1586
1714
 
1587
- [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate].each do |k|
1715
+ [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name].each do |k|
1588
1716
  hm_options[k] = options[k] if options.key? k
1589
1717
  end
1590
1718
 
1591
1719
  has_many name, scope, hm_options, &extension
1720
+ self._reflections[name.to_s].parent_reflection = [name.to_s, habtm_reflection]
1592
1721
  end
1593
1722
  end
1594
1723
  end
@@ -0,0 +1,149 @@
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 with_cast_value(name, value, type)
13
+ WithCastValue.new(name, value, type)
14
+ end
15
+
16
+ def null(name)
17
+ Null.new(name)
18
+ end
19
+
20
+ def uninitialized(name, type)
21
+ Uninitialized.new(name, type)
22
+ end
23
+ end
24
+
25
+ attr_reader :name, :value_before_type_cast, :type
26
+
27
+ # This method should not be called directly.
28
+ # Use #from_database or #from_user
29
+ def initialize(name, value_before_type_cast, type)
30
+ @name = name
31
+ @value_before_type_cast = value_before_type_cast
32
+ @type = type
33
+ end
34
+
35
+ def value
36
+ # `defined?` is cheaper than `||=` when we get back falsy values
37
+ @value = original_value unless defined?(@value)
38
+ @value
39
+ end
40
+
41
+ def original_value
42
+ type_cast(value_before_type_cast)
43
+ end
44
+
45
+ def value_for_database
46
+ type.type_cast_for_database(value)
47
+ end
48
+
49
+ def changed_from?(old_value)
50
+ type.changed?(old_value, value, value_before_type_cast)
51
+ end
52
+
53
+ def changed_in_place_from?(old_value)
54
+ type.changed_in_place?(old_value, value)
55
+ end
56
+
57
+ def with_value_from_user(value)
58
+ self.class.from_user(name, value, type)
59
+ end
60
+
61
+ def with_value_from_database(value)
62
+ self.class.from_database(name, value, type)
63
+ end
64
+
65
+ def with_cast_value(value)
66
+ self.class.with_cast_value(name, value, type)
67
+ end
68
+
69
+ def type_cast(*)
70
+ raise NotImplementedError
71
+ end
72
+
73
+ def initialized?
74
+ true
75
+ end
76
+
77
+ def ==(other)
78
+ self.class == other.class &&
79
+ name == other.name &&
80
+ value_before_type_cast == other.value_before_type_cast &&
81
+ type == other.type
82
+ end
83
+
84
+ protected
85
+
86
+ def initialize_dup(other)
87
+ if defined?(@value) && @value.duplicable?
88
+ @value = @value.dup
89
+ end
90
+ end
91
+
92
+ class FromDatabase < Attribute # :nodoc:
93
+ def type_cast(value)
94
+ type.type_cast_from_database(value)
95
+ end
96
+ end
97
+
98
+ class FromUser < Attribute # :nodoc:
99
+ def type_cast(value)
100
+ type.type_cast_from_user(value)
101
+ end
102
+ end
103
+
104
+ class WithCastValue < Attribute # :nodoc:
105
+ def type_cast(value)
106
+ value
107
+ end
108
+
109
+ def changed_in_place_from?(old_value)
110
+ false
111
+ end
112
+ end
113
+
114
+ class Null < Attribute # :nodoc:
115
+ def initialize(name)
116
+ super(name, nil, Type::Value.new)
117
+ end
118
+
119
+ def value
120
+ nil
121
+ end
122
+
123
+ def with_value_from_database(value)
124
+ raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{name}`"
125
+ end
126
+ alias_method :with_value_from_user, :with_value_from_database
127
+ end
128
+
129
+ class Uninitialized < Attribute # :nodoc:
130
+ def initialize(name, type)
131
+ super(name, nil, type)
132
+ end
133
+
134
+ def value
135
+ if block_given?
136
+ yield name
137
+ end
138
+ end
139
+
140
+ def value_for_database
141
+ end
142
+
143
+ def initialized?
144
+ false
145
+ end
146
+ end
147
+ private_constant :FromDatabase, :FromUser, :Null, :Uninitialized
148
+ end
149
+ end