activerecord 4.1.16 → 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 -2185
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +2 -1
  5. data/lib/active_record/aggregations.rb +12 -8
  6. data/lib/active_record/associations.rb +58 -33
  7. data/lib/active_record/associations/association.rb +1 -1
  8. data/lib/active_record/associations/association_scope.rb +53 -21
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +16 -5
  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 +2 -11
  13. data/lib/active_record/associations/builder/has_one.rb +2 -2
  14. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  15. data/lib/active_record/associations/collection_association.rb +32 -44
  16. data/lib/active_record/associations/collection_proxy.rb +1 -10
  17. data/lib/active_record/associations/has_many_association.rb +60 -14
  18. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  19. data/lib/active_record/associations/has_one_association.rb +0 -1
  20. data/lib/active_record/associations/join_dependency.rb +7 -9
  21. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/preloader/association.rb +9 -5
  24. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  25. data/lib/active_record/associations/singular_association.rb +16 -1
  26. data/lib/active_record/associations/through_association.rb +6 -22
  27. data/lib/active_record/attribute.rb +131 -0
  28. data/lib/active_record/attribute_assignment.rb +19 -11
  29. data/lib/active_record/attribute_decorators.rb +66 -0
  30. data/lib/active_record/attribute_methods.rb +53 -90
  31. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  32. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  33. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  34. data/lib/active_record/attribute_methods/read.rb +14 -57
  35. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  36. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  37. data/lib/active_record/attribute_methods/write.rb +8 -23
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attribute_set/builder.rb +32 -0
  40. data/lib/active_record/attributes.rb +122 -0
  41. data/lib/active_record/autosave_association.rb +11 -21
  42. data/lib/active_record/base.rb +9 -19
  43. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  44. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  53. data/lib/active_record/connection_adapters/column.rb +13 -244
  54. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  55. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  56. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  57. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  58. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  59. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  60. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  61. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  86. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  87. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  89. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  90. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  91. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  92. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  94. data/lib/active_record/connection_handling.rb +1 -1
  95. data/lib/active_record/core.rb +119 -22
  96. data/lib/active_record/counter_cache.rb +60 -6
  97. data/lib/active_record/enum.rb +9 -10
  98. data/lib/active_record/errors.rb +27 -26
  99. data/lib/active_record/explain.rb +1 -1
  100. data/lib/active_record/fixtures.rb +52 -45
  101. data/lib/active_record/gem_version.rb +3 -3
  102. data/lib/active_record/inheritance.rb +33 -8
  103. data/lib/active_record/integration.rb +4 -4
  104. data/lib/active_record/locking/optimistic.rb +34 -16
  105. data/lib/active_record/migration.rb +22 -32
  106. data/lib/active_record/migration/command_recorder.rb +19 -2
  107. data/lib/active_record/migration/join_table.rb +1 -1
  108. data/lib/active_record/model_schema.rb +39 -48
  109. data/lib/active_record/nested_attributes.rb +8 -18
  110. data/lib/active_record/persistence.rb +39 -22
  111. data/lib/active_record/query_cache.rb +3 -3
  112. data/lib/active_record/querying.rb +1 -8
  113. data/lib/active_record/railtie.rb +17 -10
  114. data/lib/active_record/railties/databases.rake +47 -42
  115. data/lib/active_record/readonly_attributes.rb +0 -1
  116. data/lib/active_record/reflection.rb +225 -92
  117. data/lib/active_record/relation.rb +35 -11
  118. data/lib/active_record/relation/batches.rb +0 -2
  119. data/lib/active_record/relation/calculations.rb +28 -32
  120. data/lib/active_record/relation/delegation.rb +1 -1
  121. data/lib/active_record/relation/finder_methods.rb +42 -20
  122. data/lib/active_record/relation/merger.rb +0 -1
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  125. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  126. data/lib/active_record/relation/query_methods.rb +98 -62
  127. data/lib/active_record/relation/spawn_methods.rb +6 -7
  128. data/lib/active_record/result.rb +16 -9
  129. data/lib/active_record/sanitization.rb +8 -1
  130. data/lib/active_record/schema.rb +0 -1
  131. data/lib/active_record/schema_dumper.rb +51 -9
  132. data/lib/active_record/schema_migration.rb +4 -0
  133. data/lib/active_record/scoping/default.rb +5 -4
  134. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  135. data/lib/active_record/statement_cache.rb +79 -5
  136. data/lib/active_record/store.rb +5 -5
  137. data/lib/active_record/tasks/database_tasks.rb +37 -5
  138. data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  140. data/lib/active_record/timestamp.rb +9 -7
  141. data/lib/active_record/transactions.rb +35 -21
  142. data/lib/active_record/type.rb +20 -0
  143. data/lib/active_record/type/binary.rb +40 -0
  144. data/lib/active_record/type/boolean.rb +19 -0
  145. data/lib/active_record/type/date.rb +46 -0
  146. data/lib/active_record/type/date_time.rb +43 -0
  147. data/lib/active_record/type/decimal.rb +40 -0
  148. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  149. data/lib/active_record/type/float.rb +19 -0
  150. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  151. data/lib/active_record/type/integer.rb +23 -0
  152. data/lib/active_record/type/mutable.rb +16 -0
  153. data/lib/active_record/type/numeric.rb +36 -0
  154. data/lib/active_record/type/serialized.rb +51 -0
  155. data/lib/active_record/type/string.rb +36 -0
  156. data/lib/active_record/type/text.rb +11 -0
  157. data/lib/active_record/type/time.rb +26 -0
  158. data/lib/active_record/type/time_value.rb +38 -0
  159. data/lib/active_record/type/type_map.rb +48 -0
  160. data/lib/active_record/type/value.rb +101 -0
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record/validations/uniqueness.rb +9 -23
  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
@@ -1,4 +1,4 @@
1
- = Active Record -- Object-relational mapping put on rails
1
+ = Active Record -- Object-relational mapping in Rails
2
2
 
3
3
  Active Record connects classes to relational database tables to establish an
4
4
  almost zero-configuration persistence layer for applications. The library
@@ -20,8 +20,10 @@ A short rundown of some of the major features:
20
20
  class Product < ActiveRecord::Base
21
21
  end
22
22
 
23
- The Product class is automatically mapped to the table named "products",
24
- which might look like this:
23
+ {Learn more}[link:classes/ActiveRecord/Base.html]
24
+
25
+ The Product class is automatically mapped to the table named "products",
26
+ which might look like this:
25
27
 
26
28
  CREATE TABLE products (
27
29
  id int(11) NOT NULL auto_increment,
@@ -29,10 +31,8 @@ A short rundown of some of the major features:
29
31
  PRIMARY KEY (id)
30
32
  );
31
33
 
32
- This would also define the following accessors: `Product#name` and
33
- `Product#name=(new_name)`
34
-
35
- {Learn more}[link:classes/ActiveRecord/Base.html]
34
+ This would also define the following accessors: `Product#name` and
35
+ `Product#name=(new_name)`.
36
36
 
37
37
 
38
38
  * Associations between objects defined by simple class methods.
@@ -130,7 +130,7 @@ A short rundown of some of the major features:
130
130
  SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
131
131
 
132
132
 
133
- * Logging support for Log4r[http://log4r.rubyforge.org] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
133
+ * Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
134
134
 
135
135
  ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
136
136
  ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
@@ -192,7 +192,7 @@ The latest version of Active Record can be installed with RubyGems:
192
192
 
193
193
  Source code can be downloaded as part of the Rails project on GitHub:
194
194
 
195
- * https://github.com/rails/rails/tree/4-1-stable/activerecord
195
+ * https://github.com/rails/rails/tree/master/activerecord
196
196
 
197
197
 
198
198
  == License
@@ -208,6 +208,11 @@ API documentation is at:
208
208
 
209
209
  * http://api.rubyonrails.org
210
210
 
211
- Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
211
+ Bug reports can be filed for the Ruby on Rails project here:
212
212
 
213
213
  * https://github.com/rails/rails/issues
214
+
215
+ Feature requests should be discussed on the rails-core mailing list here:
216
+
217
+ * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
218
+
@@ -27,10 +27,12 @@ require 'active_model'
27
27
  require 'arel'
28
28
 
29
29
  require 'active_record/version'
30
+ require 'active_record/attribute_set'
30
31
 
31
32
  module ActiveRecord
32
33
  extend ActiveSupport::Autoload
33
34
 
35
+ autoload :Attribute
34
36
  autoload :Base
35
37
  autoload :Callbacks
36
38
  autoload :Core
@@ -50,7 +52,6 @@ module ActiveRecord
50
52
  autoload :QueryCache
51
53
  autoload :Querying
52
54
  autoload :ReadonlyAttributes
53
- autoload :RecordInvalid, 'active_record/validations'
54
55
  autoload :Reflection
55
56
  autoload :RuntimeRegistry
56
57
  autoload :Sanitization
@@ -129,10 +129,10 @@ module ActiveRecord
129
129
  # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
130
130
  # converted to an instance of value class if necessary.
131
131
  #
132
- # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that
133
- # should be aggregated using the NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor
134
- # for the value class is called +create+ and it expects a CIDR address string as a parameter. New
135
- # values can be assigned to the value object using either another NetAddr::CIDR object, a string
132
+ # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be
133
+ # aggregated using the NetAddr::CIDR value class (http://www.ruby-doc.org/gems/docs/n/netaddr-1.5.0/NetAddr/CIDR.html).
134
+ # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
135
+ # New values can be assigned to the value object using either another NetAddr::CIDR object, a string
136
136
  # or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
137
137
  # these requirements:
138
138
  #
@@ -230,8 +230,8 @@ module ActiveRecord
230
230
  private
231
231
  def reader_method(name, class_name, mapping, allow_nil, constructor)
232
232
  define_method(name) do
233
- if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? })
234
- attrs = mapping.collect {|pair| read_attribute(pair.first)}
233
+ if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|key, _| !read_attribute(key).nil? })
234
+ attrs = mapping.collect {|key, _| read_attribute(key)}
235
235
  object = constructor.respond_to?(:call) ?
236
236
  constructor.call(*attrs) :
237
237
  class_name.constantize.send(constructor, *attrs)
@@ -244,15 +244,19 @@ module ActiveRecord
244
244
  def writer_method(name, class_name, mapping, allow_nil, converter)
245
245
  define_method("#{name}=") do |part|
246
246
  klass = class_name.constantize
247
+ if part.is_a?(Hash)
248
+ part = klass.new(*part.values)
249
+ end
250
+
247
251
  unless part.is_a?(klass) || converter.nil? || part.nil?
248
252
  part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
249
253
  end
250
254
 
251
255
  if part.nil? && allow_nil
252
- mapping.each { |pair| self[pair.first] = nil }
256
+ mapping.each { |key, _| self[key] = nil }
253
257
  @aggregation_cache[name] = nil
254
258
  else
255
- mapping.each { |pair| self[pair.first] = part.send(pair.last) }
259
+ mapping.each { |key, value| self[key] = part.send(value) }
256
260
  @aggregation_cache[name] = part.freeze
257
261
  end
258
262
  end
@@ -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
@@ -179,7 +179,7 @@ module ActiveRecord
179
179
  def creation_attributes
180
180
  attributes = {}
181
181
 
182
- if (reflection.macro == :has_one || reflection.macro == :has_many) && !options[:through]
182
+ if (reflection.has_one? || reflection.collection?) && !options[:through]
183
183
  attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
184
184
 
185
185
  if reflection.options[:as]
@@ -1,12 +1,34 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class AssociationScope #:nodoc:
4
- INSTANCE = new
5
-
6
4
  def self.scope(association, connection)
7
5
  INSTANCE.scope association, connection
8
6
  end
9
7
 
8
+ class BindSubstitution
9
+ def initialize(block)
10
+ @block = block
11
+ end
12
+
13
+ def bind_value(scope, column, value, alias_tracker)
14
+ substitute = alias_tracker.connection.substitute_at(
15
+ column, scope.bind_values.length)
16
+ scope.bind_values += [[column, @block.call(value)]]
17
+ substitute
18
+ end
19
+ end
20
+
21
+ def self.create(&block)
22
+ block = block ? block : lambda { |val| val }
23
+ new BindSubstitution.new(block)
24
+ end
25
+
26
+ def initialize(bind_substitution)
27
+ @bind_substitution = bind_substitution
28
+ end
29
+
30
+ INSTANCE = create
31
+
10
32
  def scope(association, connection)
11
33
  klass = association.klass
12
34
  reflection = association.reflection
@@ -22,6 +44,23 @@ module ActiveRecord
22
44
  Arel::Nodes::InnerJoin
23
45
  end
24
46
 
47
+ def self.get_bind_values(owner, chain)
48
+ bvs = []
49
+ chain.each_with_index do |reflection, i|
50
+ if reflection == chain.last
51
+ bvs << reflection.join_id_for(owner)
52
+ if reflection.type
53
+ bvs << owner.class.base_class.name
54
+ end
55
+ else
56
+ if reflection.type
57
+ bvs << chain[i + 1].klass.base_class.name
58
+ end
59
+ end
60
+ end
61
+ bvs
62
+ end
63
+
25
64
  private
26
65
 
27
66
  def construct_tables(chain, klass, refl, alias_tracker)
@@ -49,10 +88,7 @@ module ActiveRecord
49
88
  end
50
89
 
51
90
  def bind_value(scope, column, value, alias_tracker)
52
- substitute = alias_tracker.connection.substitute_at(
53
- column, scope.bind_values.length)
54
- scope.bind_values += [[column, value]]
55
- substitute
91
+ @bind_substitution.bind_value scope, column, value, alias_tracker
56
92
  end
57
93
 
58
94
  def bind(scope, table_name, column_name, value, tracker)
@@ -69,18 +105,9 @@ module ActiveRecord
69
105
  chain.each_with_index do |reflection, i|
70
106
  table, foreign_table = tables.shift, tables.first
71
107
 
72
- if reflection.source_macro == :belongs_to
73
- if reflection.options[:polymorphic]
74
- key = reflection.association_primary_key(assoc_klass)
75
- else
76
- key = reflection.association_primary_key
77
- end
78
-
79
- foreign_key = reflection.foreign_key
80
- else
81
- key = reflection.foreign_key
82
- foreign_key = reflection.active_record_primary_key
83
- end
108
+ join_keys = reflection.join_keys(assoc_klass)
109
+ key = join_keys.key
110
+ foreign_key = join_keys.foreign_key
84
111
 
85
112
  if reflection == chain.last
86
113
  bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
@@ -88,7 +115,7 @@ module ActiveRecord
88
115
 
89
116
  if reflection.type
90
117
  value = owner.class.base_class.name
91
- bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
118
+ bind_val = bind scope, table.table_name, reflection.type, value, tracker
92
119
  scope = scope.where(table[reflection.type].eq(bind_val))
93
120
  end
94
121
  else
@@ -96,7 +123,7 @@ module ActiveRecord
96
123
 
97
124
  if reflection.type
98
125
  value = chain[i + 1].klass.base_class.name
99
- bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
126
+ bind_val = bind scope, table.table_name, reflection.type, value, tracker
100
127
  scope = scope.where(table[reflection.type].eq(bind_val))
101
128
  end
102
129
 
@@ -120,6 +147,7 @@ module ActiveRecord
120
147
  end
121
148
 
122
149
  scope.where_values += item.where_values
150
+ scope.bind_values += item.bind_values
123
151
  scope.order_values |= item.order_values
124
152
  end
125
153
  end
@@ -143,7 +171,11 @@ module ActiveRecord
143
171
  end
144
172
 
145
173
  def eval_scope(klass, scope, owner)
146
- klass.unscoped.instance_exec(owner, &scope)
174
+ if scope.is_a?(Relation)
175
+ scope
176
+ else
177
+ klass.unscoped.instance_exec(owner, &scope)
178
+ end
147
179
  end
148
180
  end
149
181
  end