activerecord 4.2.11.1 → 5.0.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 (246) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1282 -1195
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record.rb +8 -4
  8. data/lib/active_record/aggregations.rb +35 -24
  9. data/lib/active_record/association_relation.rb +3 -3
  10. data/lib/active_record/associations.rb +317 -209
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +11 -9
  13. data/lib/active_record/associations/association_scope.rb +73 -102
  14. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  15. data/lib/active_record/associations/builder/association.rb +28 -34
  16. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  17. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  19. data/lib/active_record/associations/builder/has_many.rb +4 -4
  20. data/lib/active_record/associations/builder/has_one.rb +11 -6
  21. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  22. data/lib/active_record/associations/collection_association.rb +49 -41
  23. data/lib/active_record/associations/collection_proxy.rb +67 -27
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +20 -71
  26. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +29 -19
  29. data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
  30. data/lib/active_record/associations/preloader.rb +14 -4
  31. data/lib/active_record/associations/preloader/association.rb +46 -52
  32. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  33. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  35. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  36. data/lib/active_record/associations/singular_association.rb +7 -1
  37. data/lib/active_record/associations/through_association.rb +11 -3
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  40. data/lib/active_record/attribute_assignment.rb +19 -140
  41. data/lib/active_record/attribute_decorators.rb +6 -5
  42. data/lib/active_record/attribute_methods.rb +76 -47
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  44. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  45. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  46. data/lib/active_record/attribute_methods/query.rb +2 -2
  47. data/lib/active_record/attribute_methods/read.rb +31 -59
  48. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  50. data/lib/active_record/attribute_methods/write.rb +13 -37
  51. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attribute_set/builder.rb +6 -4
  54. data/lib/active_record/attributes.rb +199 -81
  55. data/lib/active_record/autosave_association.rb +49 -16
  56. data/lib/active_record/base.rb +32 -23
  57. data/lib/active_record/callbacks.rb +39 -43
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +20 -8
  60. data/lib/active_record/collection_cache_key.rb +40 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  62. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  63. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  64. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  65. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
  66. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  67. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  68. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  69. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  70. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
  71. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  72. data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
  73. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
  74. data/lib/active_record/connection_adapters/column.rb +28 -43
  75. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  76. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  77. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  78. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  79. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  80. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  84. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  85. data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
  86. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  87. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  88. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  90. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -57
  91. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  100. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +37 -14
  121. data/lib/active_record/core.rb +89 -107
  122. data/lib/active_record/counter_cache.rb +13 -24
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +113 -76
  125. data/lib/active_record/errors.rb +87 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +76 -40
  130. data/lib/active_record/gem_version.rb +4 -4
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +15 -15
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration.rb +363 -133
  140. data/lib/active_record/migration/command_recorder.rb +59 -18
  141. data/lib/active_record/migration/compatibility.rb +126 -0
  142. data/lib/active_record/model_schema.rb +129 -41
  143. data/lib/active_record/nested_attributes.rb +58 -29
  144. data/lib/active_record/null_relation.rb +16 -8
  145. data/lib/active_record/persistence.rb +121 -80
  146. data/lib/active_record/query_cache.rb +15 -18
  147. data/lib/active_record/querying.rb +10 -9
  148. data/lib/active_record/railtie.rb +23 -16
  149. data/lib/active_record/railties/controller_runtime.rb +1 -1
  150. data/lib/active_record/railties/databases.rake +69 -46
  151. data/lib/active_record/readonly_attributes.rb +1 -1
  152. data/lib/active_record/reflection.rb +282 -115
  153. data/lib/active_record/relation.rb +176 -116
  154. data/lib/active_record/relation/batches.rb +139 -34
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  156. data/lib/active_record/relation/calculations.rb +79 -108
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +163 -81
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +16 -42
  161. data/lib/active_record/relation/predicate_builder.rb +120 -107
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  163. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  164. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  166. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  167. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  168. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  169. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +308 -244
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +4 -7
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/result.rb +4 -3
  177. data/lib/active_record/runtime_registry.rb +1 -1
  178. data/lib/active_record/sanitization.rb +95 -66
  179. data/lib/active_record/schema.rb +26 -22
  180. data/lib/active_record/schema_dumper.rb +62 -38
  181. data/lib/active_record/schema_migration.rb +11 -14
  182. data/lib/active_record/scoping.rb +32 -15
  183. data/lib/active_record/scoping/default.rb +23 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/secure_token.rb +38 -0
  186. data/lib/active_record/serialization.rb +2 -4
  187. data/lib/active_record/statement_cache.rb +16 -14
  188. data/lib/active_record/store.rb +8 -3
  189. data/lib/active_record/suppressor.rb +58 -0
  190. data/lib/active_record/table_metadata.rb +68 -0
  191. data/lib/active_record/tasks/database_tasks.rb +57 -43
  192. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  193. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  194. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  195. data/lib/active_record/timestamp.rb +20 -9
  196. data/lib/active_record/touch_later.rb +58 -0
  197. data/lib/active_record/transactions.rb +138 -56
  198. data/lib/active_record/type.rb +66 -17
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -45
  201. data/lib/active_record/type/date_time.rb +2 -49
  202. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  203. data/lib/active_record/type/internal/timezone.rb +15 -0
  204. data/lib/active_record/type/serialized.rb +15 -14
  205. data/lib/active_record/type/time.rb +10 -16
  206. data/lib/active_record/type/type_map.rb +4 -4
  207. data/lib/active_record/type_caster.rb +7 -0
  208. data/lib/active_record/type_caster/connection.rb +29 -0
  209. data/lib/active_record/type_caster/map.rb +19 -0
  210. data/lib/active_record/validations.rb +33 -32
  211. data/lib/active_record/validations/absence.rb +23 -0
  212. data/lib/active_record/validations/associated.rb +10 -3
  213. data/lib/active_record/validations/length.rb +24 -0
  214. data/lib/active_record/validations/presence.rb +11 -12
  215. data/lib/active_record/validations/uniqueness.rb +30 -29
  216. data/lib/rails/generators/active_record/migration.rb +7 -0
  217. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  218. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  219. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  220. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  221. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  222. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  223. metadata +59 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  225. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  226. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  227. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  228. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  229. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  231. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  232. data/lib/active_record/type/big_integer.rb +0 -13
  233. data/lib/active_record/type/binary.rb +0 -50
  234. data/lib/active_record/type/boolean.rb +0 -31
  235. data/lib/active_record/type/decimal.rb +0 -64
  236. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  237. data/lib/active_record/type/decorator.rb +0 -14
  238. data/lib/active_record/type/float.rb +0 -19
  239. data/lib/active_record/type/integer.rb +0 -59
  240. data/lib/active_record/type/mutable.rb +0 -16
  241. data/lib/active_record/type/numeric.rb +0 -36
  242. data/lib/active_record/type/string.rb +0 -40
  243. data/lib/active_record/type/text.rb +0 -11
  244. data/lib/active_record/type/time_value.rb +0 -38
  245. data/lib/active_record/type/unsigned_integer.rb +0 -15
  246. data/lib/active_record/type/value.rb +0 -110
@@ -1,5 +1,3 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
-
3
1
  # This is the parent Association class which defines the variables
4
2
  # used by all associations.
5
3
  #
@@ -11,19 +9,14 @@ require 'active_support/core_ext/module/attribute_accessors'
11
9
  # - CollectionAssociation
12
10
  # - HasManyAssociation
13
11
 
14
- module ActiveRecord::Associations::Builder
12
+ module ActiveRecord::Associations::Builder # :nodoc:
15
13
  class Association #:nodoc:
16
14
  class << self
17
15
  attr_accessor :extensions
18
- # TODO: This class accessor is needed to make activerecord-deprecated_finders work.
19
- # We can move it to a constant in 5.0.
20
- attr_accessor :valid_options
21
16
  end
22
17
  self.extensions = []
23
18
 
24
- self.valid_options = [:class_name, :anonymous_class, :foreign_key, :validate]
25
-
26
- attr_reader :name, :scope, :options
19
+ VALID_OPTIONS = [:class_name, :anonymous_class, :foreign_key, :validate] # :nodoc:
27
20
 
28
21
  def self.build(model, name, scope, options, &block)
29
22
  if model.dangerous_attribute_method?(name)
@@ -32,57 +25,60 @@ module ActiveRecord::Associations::Builder
32
25
  "Please choose a different association name."
33
26
  end
34
27
 
35
- builder = create_builder model, name, scope, options, &block
36
- reflection = builder.build(model)
28
+ extension = define_extensions model, name, &block
29
+ reflection = create_reflection model, name, scope, options, extension
37
30
  define_accessors model, reflection
38
31
  define_callbacks model, reflection
39
32
  define_validations model, reflection
40
- builder.define_extensions model
41
33
  reflection
42
34
  end
43
35
 
44
- def self.create_builder(model, name, scope, options, &block)
36
+ def self.create_reflection(model, name, scope, options, extension = nil)
45
37
  raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
46
38
 
47
- new(model, name, scope, options, &block)
48
- end
49
-
50
- def initialize(model, name, scope, options)
51
- # TODO: Move this to create_builder as soon we drop support to activerecord-deprecated_finders.
52
39
  if scope.is_a?(Hash)
53
40
  options = scope
54
41
  scope = nil
55
42
  end
56
43
 
57
- # TODO: Remove this model argument as soon we drop support to activerecord-deprecated_finders.
58
- @name = name
59
- @scope = scope
60
- @options = options
44
+ validate_options(options)
61
45
 
62
- validate_options
46
+ scope = build_scope(scope, extension)
47
+
48
+ ActiveRecord::Reflection.create(macro, name, scope, options, model)
49
+ end
50
+
51
+ def self.build_scope(scope, extension)
52
+ new_scope = scope
63
53
 
64
54
  if scope && scope.arity == 0
65
- @scope = proc { instance_exec(&scope) }
55
+ new_scope = proc { instance_exec(&scope) }
56
+ end
57
+
58
+ if extension
59
+ new_scope = wrap_scope new_scope, extension
66
60
  end
61
+
62
+ new_scope
67
63
  end
68
64
 
69
- def build(model)
70
- ActiveRecord::Reflection.create(macro, name, scope, options, model)
65
+ def self.wrap_scope(scope, extension)
66
+ scope
71
67
  end
72
68
 
73
- def macro
69
+ def self.macro
74
70
  raise NotImplementedError
75
71
  end
76
72
 
77
- def valid_options
78
- Association.valid_options + Association.extensions.flat_map(&:valid_options)
73
+ def self.valid_options(options)
74
+ VALID_OPTIONS + Association.extensions.flat_map(&:valid_options)
79
75
  end
80
76
 
81
- def validate_options
82
- options.assert_valid_keys(valid_options)
77
+ def self.validate_options(options)
78
+ options.assert_valid_keys(valid_options(options))
83
79
  end
84
80
 
85
- def define_extensions(model)
81
+ def self.define_extensions(model, name)
86
82
  end
87
83
 
88
84
  def self.define_callbacks(model, reflection)
@@ -133,8 +129,6 @@ module ActiveRecord::Associations::Builder
133
129
  raise NotImplementedError
134
130
  end
135
131
 
136
- private
137
-
138
132
  def self.check_dependent_options(dependent)
139
133
  unless valid_dependent_options.include? dependent
140
134
  raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
@@ -1,11 +1,11 @@
1
- module ActiveRecord::Associations::Builder
1
+ module ActiveRecord::Associations::Builder # :nodoc:
2
2
  class BelongsTo < SingularAssociation #:nodoc:
3
- def macro
3
+ def self.macro
4
4
  :belongs_to
5
5
  end
6
6
 
7
- def valid_options
8
- super + [:foreign_type, :polymorphic, :touch, :counter_cache]
7
+ def self.valid_options(options)
8
+ super + [:polymorphic, :touch, :counter_cache, :optional]
9
9
  end
10
10
 
11
11
  def self.valid_dependent_options
@@ -23,8 +23,6 @@ module ActiveRecord::Associations::Builder
23
23
  add_counter_cache_methods mixin
24
24
  end
25
25
 
26
- private
27
-
28
26
  def self.add_counter_cache_methods(mixin)
29
27
  return if mixin.method_defined? :belongs_to_counter_cache_after_update
30
28
 
@@ -35,16 +33,26 @@ module ActiveRecord::Associations::Builder
35
33
 
36
34
  if (@_after_create_counter_called ||= false)
37
35
  @_after_create_counter_called = false
38
- elsif attribute_changed?(foreign_key) && !new_record? && reflection.constructable?
39
- model = reflection.klass
36
+ elsif (@_after_replace_counter_called ||= false)
37
+ @_after_replace_counter_called = false
38
+ elsif attribute_changed?(foreign_key) && !new_record?
39
+ if reflection.polymorphic?
40
+ model = attribute(reflection.foreign_type).try(:constantize)
41
+ model_was = attribute_was(reflection.foreign_type).try(:constantize)
42
+ else
43
+ model = reflection.klass
44
+ model_was = reflection.klass
45
+ end
46
+
40
47
  foreign_key_was = attribute_was foreign_key
41
48
  foreign_key = attribute foreign_key
42
49
 
43
50
  if foreign_key && model.respond_to?(:increment_counter)
44
51
  model.increment_counter(cache_column, foreign_key)
45
52
  end
46
- if foreign_key_was && model.respond_to?(:decrement_counter)
47
- model.decrement_counter(cache_column, foreign_key_was)
53
+
54
+ if foreign_key_was && model_was.respond_to?(:decrement_counter)
55
+ model_was.decrement_counter(cache_column, foreign_key_was)
48
56
  end
49
57
  end
50
58
  end
@@ -62,7 +70,7 @@ module ActiveRecord::Associations::Builder
62
70
  klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
63
71
  end
64
72
 
65
- def self.touch_record(o, foreign_key, name, touch) # :nodoc:
73
+ def self.touch_record(o, foreign_key, name, touch, touch_method) # :nodoc:
66
74
  old_foreign_id = o.changed_attributes[foreign_key]
67
75
 
68
76
  if old_foreign_id
@@ -77,9 +85,9 @@ module ActiveRecord::Associations::Builder
77
85
 
78
86
  if old_record
79
87
  if touch != true
80
- old_record.touch touch
88
+ old_record.send(touch_method, touch)
81
89
  else
82
- old_record.touch
90
+ old_record.send(touch_method)
83
91
  end
84
92
  end
85
93
  end
@@ -87,9 +95,9 @@ module ActiveRecord::Associations::Builder
87
95
  record = o.send name
88
96
  if record && record.persisted?
89
97
  if touch != true
90
- record.touch touch
98
+ record.send(touch_method, touch)
91
99
  else
92
- record.touch
100
+ record.send(touch_method)
93
101
  end
94
102
  end
95
103
  end
@@ -100,7 +108,7 @@ module ActiveRecord::Associations::Builder
100
108
  touch = reflection.options[:touch]
101
109
 
102
110
  callback = lambda { |record|
103
- BelongsTo.touch_record(record, foreign_key, n, touch)
111
+ BelongsTo.touch_record(record, foreign_key, n, touch, belongs_to_touch_method)
104
112
  }
105
113
 
106
114
  model.after_save callback, if: :changed?
@@ -109,8 +117,25 @@ module ActiveRecord::Associations::Builder
109
117
  end
110
118
 
111
119
  def self.add_destroy_callbacks(model, reflection)
112
- name = reflection.name
113
- model.after_destroy lambda { |o| o.association(name).handle_dependency }
120
+ model.after_destroy lambda { |o| o.association(reflection.name).handle_dependency }
121
+ end
122
+
123
+ def self.define_validations(model, reflection)
124
+ if reflection.options.key?(:required)
125
+ reflection.options[:optional] = !reflection.options.delete(:required)
126
+ end
127
+
128
+ if reflection.options[:optional].nil?
129
+ required = model.belongs_to_required_by_default
130
+ else
131
+ required = !reflection.options[:optional]
132
+ end
133
+
134
+ super
135
+
136
+ if required
137
+ model.validates_presence_of reflection.name, message: :required
138
+ end
114
139
  end
115
140
  end
116
141
  end
@@ -2,27 +2,16 @@
2
2
 
3
3
  require 'active_record/associations'
4
4
 
5
- module ActiveRecord::Associations::Builder
5
+ module ActiveRecord::Associations::Builder # :nodoc:
6
6
  class CollectionAssociation < Association #:nodoc:
7
7
 
8
8
  CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
9
9
 
10
- def valid_options
10
+ def self.valid_options(options)
11
11
  super + [:table_name, :before_add,
12
12
  :after_add, :before_remove, :after_remove, :extend]
13
13
  end
14
14
 
15
- attr_reader :block_extension
16
-
17
- def initialize(model, name, scope, options)
18
- super
19
- @mod = nil
20
- if block_given?
21
- @mod = Module.new(&Proc.new)
22
- @scope = wrap_scope @scope, @mod
23
- end
24
- end
25
-
26
15
  def self.define_callbacks(model, reflection)
27
16
  super
28
17
  name = reflection.name
@@ -32,10 +21,11 @@ module ActiveRecord::Associations::Builder
32
21
  }
33
22
  end
34
23
 
35
- def define_extensions(model)
36
- if @mod
24
+ def self.define_extensions(model, name)
25
+ if block_given?
37
26
  extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
38
- model.parent.const_set(extension_module_name, @mod)
27
+ extension = Module.new(&Proc.new)
28
+ model.parent.const_set(extension_module_name, extension)
39
29
  end
40
30
  end
41
31
 
@@ -78,9 +68,7 @@ module ActiveRecord::Associations::Builder
78
68
  CODE
79
69
  end
80
70
 
81
- private
82
-
83
- def wrap_scope(scope, mod)
71
+ def self.wrap_scope(scope, mod)
84
72
  if scope
85
73
  if scope.arity > 0
86
74
  proc { |owner| instance_exec(owner, &scope).extending(mod) }
@@ -1,9 +1,9 @@
1
- module ActiveRecord::Associations::Builder
1
+ module ActiveRecord::Associations::Builder # :nodoc:
2
2
  class HasAndBelongsToMany # :nodoc:
3
- class JoinTableResolver
3
+ class JoinTableResolver # :nodoc:
4
4
  KnownTable = Struct.new :join_table
5
5
 
6
- class KnownClass
6
+ class KnownClass # :nodoc:
7
7
  def initialize(lhs_class, rhs_class_name)
8
8
  @lhs_class = lhs_class
9
9
  @rhs_class_name = rhs_class_name
@@ -62,13 +62,13 @@ module ActiveRecord::Associations::Builder
62
62
  end
63
63
 
64
64
  def self.add_left_association(name, options)
65
- belongs_to name, options
65
+ belongs_to name, required: false, **options
66
66
  self.left_reflection = _reflect_on_association(name)
67
67
  end
68
68
 
69
69
  def self.add_right_association(name, options)
70
70
  rhs_name = name.to_s.singularize.to_sym
71
- belongs_to rhs_name, options
71
+ belongs_to rhs_name, required: false, **options
72
72
  self.right_reflection = _reflect_on_association(rhs_name)
73
73
  end
74
74
 
@@ -76,6 +76,9 @@ module ActiveRecord::Associations::Builder
76
76
  left_model.retrieve_connection
77
77
  end
78
78
 
79
+ def self.primary_key
80
+ false
81
+ end
79
82
  }
80
83
 
81
84
  join_model.name = "HABTM_#{association_name.to_s.camelize}"
@@ -89,13 +92,13 @@ module ActiveRecord::Associations::Builder
89
92
 
90
93
  def middle_reflection(join_model)
91
94
  middle_name = [lhs_model.name.downcase.pluralize,
92
- association_name].join('_').gsub(/::/, '_').to_sym
95
+ association_name].join('_'.freeze).gsub('::'.freeze, '_'.freeze).to_sym
93
96
  middle_options = middle_options join_model
94
- hm_builder = HasMany.create_builder(lhs_model,
95
- middle_name,
96
- nil,
97
- middle_options)
98
- hm_builder.build lhs_model
97
+
98
+ HasMany.create_reflection(lhs_model,
99
+ middle_name,
100
+ nil,
101
+ middle_options)
99
102
  end
100
103
 
101
104
  private
@@ -1,11 +1,11 @@
1
- module ActiveRecord::Associations::Builder
1
+ module ActiveRecord::Associations::Builder # :nodoc:
2
2
  class HasMany < CollectionAssociation #:nodoc:
3
- def macro
3
+ def self.macro
4
4
  :has_many
5
5
  end
6
6
 
7
- def valid_options
8
- super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type]
7
+ def self.valid_options(options)
8
+ super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type, :index_errors]
9
9
  end
10
10
 
11
11
  def self.valid_dependent_options
@@ -1,11 +1,11 @@
1
- module ActiveRecord::Associations::Builder
1
+ module ActiveRecord::Associations::Builder # :nodoc:
2
2
  class HasOne < SingularAssociation #:nodoc:
3
- def macro
3
+ def self.macro
4
4
  :has_one
5
5
  end
6
6
 
7
- def valid_options
8
- valid = super + [:as, :foreign_type]
7
+ def self.valid_options(options)
8
+ valid = super + [:as]
9
9
  valid += [:through, :source, :source_type] if options[:through]
10
10
  valid
11
11
  end
@@ -14,10 +14,15 @@ module ActiveRecord::Associations::Builder
14
14
  [:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception]
15
15
  end
16
16
 
17
- private
18
-
19
17
  def self.add_destroy_callbacks(model, reflection)
20
18
  super unless reflection.options[:through]
21
19
  end
20
+
21
+ def self.define_validations(model, reflection)
22
+ super
23
+ if reflection.options[:required]
24
+ model.validates_presence_of reflection.name, message: :required
25
+ end
26
+ end
22
27
  end
23
28
  end
@@ -1,9 +1,9 @@
1
1
  # This class is inherited by the has_one and belongs_to association classes
2
2
 
3
- module ActiveRecord::Associations::Builder
3
+ module ActiveRecord::Associations::Builder # :nodoc:
4
4
  class SingularAssociation < Association #:nodoc:
5
- def valid_options
6
- super + [:dependent, :primary_key, :inverse_of, :required]
5
+ def self.valid_options(options)
6
+ super + [:foreign_type, :dependent, :primary_key, :inverse_of, :required]
7
7
  end
8
8
 
9
9
  def self.define_accessors(model, reflection)
@@ -27,12 +27,5 @@ module ActiveRecord::Associations::Builder
27
27
  end
28
28
  CODE
29
29
  end
30
-
31
- def self.define_validations(model, reflection)
32
- super
33
- if reflection.options[:required]
34
- model.validates_presence_of reflection.name
35
- end
36
- end
37
30
  end
38
31
  end
@@ -28,15 +28,21 @@ module ActiveRecord
28
28
  # Implements the reader method, e.g. foo.items for Foo.has_many :items
29
29
  def reader(force_reload = false)
30
30
  if force_reload
31
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
32
+ Passing an argument to force an association to reload is now
33
+ deprecated and will be removed in Rails 5.1. Please call `reload`
34
+ on the result collection proxy instead.
35
+ MSG
36
+
31
37
  klass.uncached { reload }
32
38
  elsif stale_target?
33
39
  reload
34
40
  end
35
41
 
36
- if owner.new_record?
42
+ if null_scope?
37
43
  # Cache the proxy separately before the owner has an id
38
44
  # or else a post-save proxy will still lack the id
39
- @new_record_proxy ||= CollectionProxy.create(klass, self)
45
+ @null_proxy ||= CollectionProxy.create(klass, self)
40
46
  else
41
47
  @proxy ||= CollectionProxy.create(klass, self)
42
48
  end
@@ -54,28 +60,22 @@ module ActiveRecord
54
60
  record.send(reflection.association_primary_key)
55
61
  end
56
62
  else
57
- column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
58
- scope.pluck(column)
63
+ @association_ids ||= (
64
+ column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
65
+ scope.pluck(column)
66
+ )
59
67
  end
60
68
  end
61
69
 
62
70
  # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
63
71
  def ids_writer(ids)
64
- pk_column = reflection.association_primary_key
65
- pk_type = klass.type_for_attribute(pk_column)
66
- ids = Array(ids).reject(&:blank?).map do |i|
67
- pk_type.type_cast_from_user(i)
68
- end
69
-
70
- objs = klass.where(pk_column => ids).index_by do |r|
71
- r.send(pk_column)
72
- end.values_at(*ids).compact
73
-
74
- if objs.size == ids.size
75
- replace(objs.index_by { |r| r.send(pk_column) }.values_at(*ids))
76
- else
77
- klass.all.raise_record_not_found_exception!(ids, objs.size, ids.size)
78
- end
72
+ pk_type = reflection.primary_key_type
73
+ ids = Array(ids).reject(&:blank?)
74
+ ids.map! { |i| pk_type.cast(i) }
75
+ records = klass.where(reflection.association_primary_key => ids).index_by do |r|
76
+ r.send(reflection.association_primary_key)
77
+ end.values_at(*ids)
78
+ replace(records)
79
79
  end
80
80
 
81
81
  def reset
@@ -136,6 +136,14 @@ module ActiveRecord
136
136
  first_nth_or_last(:forty_two, *args)
137
137
  end
138
138
 
139
+ def third_to_last(*args)
140
+ first_nth_or_last(:third_to_last, *args)
141
+ end
142
+
143
+ def second_to_last(*args)
144
+ first_nth_or_last(:second_to_last, *args)
145
+ end
146
+
139
147
  def last(*args)
140
148
  first_nth_or_last(:last, *args)
141
149
  end
@@ -172,6 +180,7 @@ module ActiveRecord
172
180
  # be chained. Since << flattens its argument list and inserts each record,
173
181
  # +push+ and +concat+ behave identically.
174
182
  def concat(*records)
183
+ records = records.flatten
175
184
  if owner.new_record?
176
185
  load_target
177
186
  concat_records(records)
@@ -239,11 +248,7 @@ module ActiveRecord
239
248
 
240
249
  # Count all records using SQL. Construct options and pass them with
241
250
  # scope to the target class's +count+.
242
- def count(column_name = nil, count_options = {})
243
- # TODO: Remove count_options argument as soon we remove support to
244
- # activerecord-deprecated_finders.
245
- column_name, count_options = nil, column_name if column_name.is_a?(Hash)
246
-
251
+ def count(column_name = nil)
247
252
  relation = scope
248
253
  if association_scope.distinct_value
249
254
  # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
@@ -310,7 +315,7 @@ module ActiveRecord
310
315
  elsif !loaded? && !association_scope.group_values.empty?
311
316
  load_target.size
312
317
  elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
313
- unsaved_records = target.select { |r| r.new_record? }
318
+ unsaved_records = target.select(&:new_record?)
314
319
  unsaved_records.size + count_records
315
320
  else
316
321
  count_records
@@ -343,7 +348,8 @@ module ActiveRecord
343
348
  end
344
349
 
345
350
  # Returns true if the collections is not empty.
346
- # Equivalent to +!collection.empty?+.
351
+ # If block given, loads all records and checks for one or more matches.
352
+ # Otherwise, equivalent to +!collection.empty?+.
347
353
  def any?
348
354
  if block_given?
349
355
  load_target.any? { |*block_args| yield(*block_args) }
@@ -353,7 +359,8 @@ module ActiveRecord
353
359
  end
354
360
 
355
361
  # Returns true if the collection has more than 1 record.
356
- # Equivalent to +collection.size > 1+.
362
+ # If block given, loads all records and checks for two or more matches.
363
+ # Otherwise, equivalent to +collection.size > 1+.
357
364
  def many?
358
365
  if block_given?
359
366
  load_target.many? { |*block_args| yield(*block_args) }
@@ -382,6 +389,8 @@ module ActiveRecord
382
389
  replace_common_records_in_memory(other_array, original_target)
383
390
  if other_array != original_target
384
391
  transaction { replace_records(other_array, original_target) }
392
+ else
393
+ other_array
385
394
  end
386
395
  end
387
396
  end
@@ -416,12 +425,16 @@ module ActiveRecord
416
425
 
417
426
  def replace_on_target(record, index, skip_callbacks)
418
427
  callback(:before_add, record) unless skip_callbacks
428
+
429
+ was_loaded = loaded?
419
430
  yield(record) if block_given?
420
431
 
421
- if index
422
- @target[index] = record
423
- else
424
- @target << record
432
+ unless !was_loaded && loaded?
433
+ if index
434
+ @target[index] = record
435
+ else
436
+ @target << record
437
+ end
425
438
  end
426
439
 
427
440
  callback(:after_add, record) unless skip_callbacks
@@ -521,7 +534,7 @@ module ActiveRecord
521
534
  def delete_or_destroy(records, method)
522
535
  records = records.flatten
523
536
  records.each { |record| raise_on_type_mismatch!(record) }
524
- existing_records = records.reject { |r| r.new_record? }
537
+ existing_records = records.reject(&:new_record?)
525
538
 
526
539
  if existing_records.empty?
527
540
  remove_records(existing_records, records, method)
@@ -568,7 +581,7 @@ module ActiveRecord
568
581
  def concat_records(records, should_raise = false)
569
582
  result = true
570
583
 
571
- records.flatten.each do |record|
584
+ records.each do |record|
572
585
  raise_on_type_mismatch!(record)
573
586
  add_to_target(record) do |rec|
574
587
  result &&= insert_record(rec, true, should_raise) unless owner.new_record?
@@ -612,13 +625,8 @@ module ActiveRecord
612
625
  if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
613
626
  assoc = owner.association(reflection.through_reflection.name)
614
627
  assoc.reader.any? { |source|
615
- target_association = source.send(reflection.source_reflection.name)
616
-
617
- if target_association.respond_to?(:include?)
618
- target_association.include?(record)
619
- else
620
- target_association == record
621
- end
628
+ target_reflection = source.send(reflection.source_reflection.name)
629
+ target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
622
630
  } || target.include?(record)
623
631
  else
624
632
  target.include?(record)
@@ -629,7 +637,7 @@ module ActiveRecord
629
637
  # specified, then #find scans the entire collection.
630
638
  def find_by_scan(*args)
631
639
  expects_array = args.first.kind_of?(Array)
632
- ids = args.flatten.compact.map{ |arg| arg.to_s }.uniq
640
+ ids = args.flatten.compact.map(&:to_s).uniq
633
641
 
634
642
  if ids.size == 1
635
643
  id = ids.first