activerecord 4.2.11.3 → 5.0.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 (229) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1029 -1349
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record.rb +7 -3
  7. data/lib/active_record/aggregations.rb +35 -25
  8. data/lib/active_record/association_relation.rb +2 -2
  9. data/lib/active_record/associations.rb +305 -204
  10. data/lib/active_record/associations/alias_tracker.rb +19 -16
  11. data/lib/active_record/associations/association.rb +10 -8
  12. data/lib/active_record/associations/association_scope.rb +73 -102
  13. data/lib/active_record/associations/belongs_to_association.rb +20 -32
  14. data/lib/active_record/associations/builder/association.rb +28 -34
  15. data/lib/active_record/associations/builder/belongs_to.rb +41 -18
  16. data/lib/active_record/associations/builder/collection_association.rb +8 -24
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
  18. data/lib/active_record/associations/builder/has_many.rb +4 -4
  19. data/lib/active_record/associations/builder/has_one.rb +10 -5
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -9
  21. data/lib/active_record/associations/collection_association.rb +40 -43
  22. data/lib/active_record/associations/collection_proxy.rb +55 -29
  23. data/lib/active_record/associations/foreign_association.rb +1 -1
  24. data/lib/active_record/associations/has_many_association.rb +20 -71
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -52
  26. data/lib/active_record/associations/has_one_association.rb +12 -5
  27. data/lib/active_record/associations/join_dependency.rb +28 -18
  28. data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
  29. data/lib/active_record/associations/preloader.rb +13 -4
  30. data/lib/active_record/associations/preloader/association.rb +45 -51
  31. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +5 -4
  35. data/lib/active_record/associations/singular_association.rb +6 -0
  36. data/lib/active_record/associations/through_association.rb +11 -3
  37. data/lib/active_record/attribute.rb +61 -17
  38. data/lib/active_record/attribute/user_provided_default.rb +23 -0
  39. data/lib/active_record/attribute_assignment.rb +27 -140
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods.rb +79 -26
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  44. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  45. data/lib/active_record/attribute_methods/query.rb +2 -2
  46. data/lib/active_record/attribute_methods/read.rb +26 -42
  47. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
  49. data/lib/active_record/attribute_methods/write.rb +13 -24
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set.rb +30 -3
  52. data/lib/active_record/attribute_set/builder.rb +6 -4
  53. data/lib/active_record/attributes.rb +194 -81
  54. data/lib/active_record/autosave_association.rb +33 -15
  55. data/lib/active_record/base.rb +30 -18
  56. data/lib/active_record/callbacks.rb +36 -40
  57. data/lib/active_record/coders/yaml_column.rb +20 -8
  58. data/lib/active_record/collection_cache_key.rb +31 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
  62. data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
  70. data/lib/active_record/connection_adapters/column.rb +27 -41
  71. data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  85. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  86. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  87. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
  103. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
  107. data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
  108. data/lib/active_record/connection_handling.rb +5 -5
  109. data/lib/active_record/core.rb +72 -104
  110. data/lib/active_record/counter_cache.rb +9 -20
  111. data/lib/active_record/dynamic_matchers.rb +1 -20
  112. data/lib/active_record/enum.rb +110 -76
  113. data/lib/active_record/errors.rb +72 -47
  114. data/lib/active_record/explain_registry.rb +1 -1
  115. data/lib/active_record/explain_subscriber.rb +1 -1
  116. data/lib/active_record/fixture_set/file.rb +19 -4
  117. data/lib/active_record/fixtures.rb +76 -40
  118. data/lib/active_record/gem_version.rb +4 -4
  119. data/lib/active_record/inheritance.rb +27 -40
  120. data/lib/active_record/integration.rb +4 -4
  121. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  122. data/lib/active_record/locale/en.yml +3 -2
  123. data/lib/active_record/locking/optimistic.rb +10 -14
  124. data/lib/active_record/locking/pessimistic.rb +1 -1
  125. data/lib/active_record/log_subscriber.rb +40 -22
  126. data/lib/active_record/migration.rb +304 -133
  127. data/lib/active_record/migration/command_recorder.rb +59 -18
  128. data/lib/active_record/migration/compatibility.rb +90 -0
  129. data/lib/active_record/model_schema.rb +92 -40
  130. data/lib/active_record/nested_attributes.rb +45 -34
  131. data/lib/active_record/null_relation.rb +15 -7
  132. data/lib/active_record/persistence.rb +112 -72
  133. data/lib/active_record/querying.rb +6 -5
  134. data/lib/active_record/railtie.rb +20 -13
  135. data/lib/active_record/railties/controller_runtime.rb +1 -1
  136. data/lib/active_record/railties/databases.rake +47 -38
  137. data/lib/active_record/readonly_attributes.rb +1 -1
  138. data/lib/active_record/reflection.rb +182 -57
  139. data/lib/active_record/relation.rb +152 -100
  140. data/lib/active_record/relation/batches.rb +133 -33
  141. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  142. data/lib/active_record/relation/calculations.rb +80 -101
  143. data/lib/active_record/relation/delegation.rb +6 -19
  144. data/lib/active_record/relation/finder_methods.rb +58 -46
  145. data/lib/active_record/relation/from_clause.rb +32 -0
  146. data/lib/active_record/relation/merger.rb +13 -42
  147. data/lib/active_record/relation/predicate_builder.rb +99 -105
  148. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  149. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
  150. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  151. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  152. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  153. data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
  154. data/lib/active_record/relation/query_attribute.rb +19 -0
  155. data/lib/active_record/relation/query_methods.rb +274 -238
  156. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  157. data/lib/active_record/relation/spawn_methods.rb +3 -6
  158. data/lib/active_record/relation/where_clause.rb +173 -0
  159. data/lib/active_record/relation/where_clause_factory.rb +37 -0
  160. data/lib/active_record/result.rb +4 -3
  161. data/lib/active_record/runtime_registry.rb +1 -1
  162. data/lib/active_record/sanitization.rb +94 -65
  163. data/lib/active_record/schema.rb +23 -22
  164. data/lib/active_record/schema_dumper.rb +33 -22
  165. data/lib/active_record/schema_migration.rb +10 -4
  166. data/lib/active_record/scoping.rb +17 -6
  167. data/lib/active_record/scoping/default.rb +19 -6
  168. data/lib/active_record/scoping/named.rb +39 -28
  169. data/lib/active_record/secure_token.rb +38 -0
  170. data/lib/active_record/serialization.rb +2 -4
  171. data/lib/active_record/statement_cache.rb +15 -13
  172. data/lib/active_record/store.rb +8 -3
  173. data/lib/active_record/suppressor.rb +54 -0
  174. data/lib/active_record/table_metadata.rb +64 -0
  175. data/lib/active_record/tasks/database_tasks.rb +30 -40
  176. data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
  177. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  178. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  179. data/lib/active_record/timestamp.rb +16 -9
  180. data/lib/active_record/touch_later.rb +58 -0
  181. data/lib/active_record/transactions.rb +138 -56
  182. data/lib/active_record/type.rb +66 -17
  183. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  184. data/lib/active_record/type/date.rb +2 -45
  185. data/lib/active_record/type/date_time.rb +2 -49
  186. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  187. data/lib/active_record/type/internal/timezone.rb +15 -0
  188. data/lib/active_record/type/serialized.rb +9 -14
  189. data/lib/active_record/type/time.rb +3 -21
  190. data/lib/active_record/type/type_map.rb +4 -4
  191. data/lib/active_record/type_caster.rb +7 -0
  192. data/lib/active_record/type_caster/connection.rb +29 -0
  193. data/lib/active_record/type_caster/map.rb +19 -0
  194. data/lib/active_record/validations.rb +33 -32
  195. data/lib/active_record/validations/absence.rb +24 -0
  196. data/lib/active_record/validations/associated.rb +10 -3
  197. data/lib/active_record/validations/length.rb +36 -0
  198. data/lib/active_record/validations/presence.rb +12 -12
  199. data/lib/active_record/validations/uniqueness.rb +24 -21
  200. data/lib/rails/generators/active_record/migration.rb +7 -0
  201. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  202. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  203. data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
  204. data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
  205. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  206. metadata +50 -35
  207. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  208. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  209. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  210. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  211. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  212. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  213. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  214. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  215. data/lib/active_record/type/big_integer.rb +0 -13
  216. data/lib/active_record/type/binary.rb +0 -50
  217. data/lib/active_record/type/boolean.rb +0 -31
  218. data/lib/active_record/type/decimal.rb +0 -64
  219. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  220. data/lib/active_record/type/decorator.rb +0 -14
  221. data/lib/active_record/type/float.rb +0 -19
  222. data/lib/active_record/type/integer.rb +0 -59
  223. data/lib/active_record/type/mutable.rb +0 -16
  224. data/lib/active_record/type/numeric.rb +0 -36
  225. data/lib/active_record/type/string.rb +0 -40
  226. data/lib/active_record/type/text.rb +0 -11
  227. data/lib/active_record/type/time_value.rb +0 -38
  228. data/lib/active_record/type/unsigned_integer.rb +0 -15
  229. 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 + [:foreign_type, :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,24 @@ 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 attribute_changed?(foreign_key) && !new_record?
37
+ if reflection.polymorphic?
38
+ model = attribute(reflection.foreign_type).try(:constantize)
39
+ model_was = attribute_was(reflection.foreign_type).try(:constantize)
40
+ else
41
+ model = reflection.klass
42
+ model_was = reflection.klass
43
+ end
44
+
40
45
  foreign_key_was = attribute_was foreign_key
41
46
  foreign_key = attribute foreign_key
42
47
 
43
48
  if foreign_key && model.respond_to?(:increment_counter)
44
49
  model.increment_counter(cache_column, foreign_key)
45
50
  end
46
- if foreign_key_was && model.respond_to?(:decrement_counter)
47
- model.decrement_counter(cache_column, foreign_key_was)
51
+
52
+ if foreign_key_was && model_was.respond_to?(:decrement_counter)
53
+ model_was.decrement_counter(cache_column, foreign_key_was)
48
54
  end
49
55
  end
50
56
  end
@@ -62,7 +68,7 @@ module ActiveRecord::Associations::Builder
62
68
  klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
63
69
  end
64
70
 
65
- def self.touch_record(o, foreign_key, name, touch) # :nodoc:
71
+ def self.touch_record(o, foreign_key, name, touch, touch_method) # :nodoc:
66
72
  old_foreign_id = o.changed_attributes[foreign_key]
67
73
 
68
74
  if old_foreign_id
@@ -77,9 +83,9 @@ module ActiveRecord::Associations::Builder
77
83
 
78
84
  if old_record
79
85
  if touch != true
80
- old_record.touch touch
86
+ old_record.send(touch_method, touch)
81
87
  else
82
- old_record.touch
88
+ old_record.send(touch_method)
83
89
  end
84
90
  end
85
91
  end
@@ -87,9 +93,9 @@ module ActiveRecord::Associations::Builder
87
93
  record = o.send name
88
94
  if record && record.persisted?
89
95
  if touch != true
90
- record.touch touch
96
+ record.send(touch_method, touch)
91
97
  else
92
- record.touch
98
+ record.send(touch_method)
93
99
  end
94
100
  end
95
101
  end
@@ -100,7 +106,7 @@ module ActiveRecord::Associations::Builder
100
106
  touch = reflection.options[:touch]
101
107
 
102
108
  callback = lambda { |record|
103
- BelongsTo.touch_record(record, foreign_key, n, touch)
109
+ BelongsTo.touch_record(record, foreign_key, n, touch, belongs_to_touch_method)
104
110
  }
105
111
 
106
112
  model.after_save callback, if: :changed?
@@ -109,8 +115,25 @@ module ActiveRecord::Associations::Builder
109
115
  end
110
116
 
111
117
  def self.add_destroy_callbacks(model, reflection)
112
- name = reflection.name
113
- model.after_destroy lambda { |o| o.association(name).handle_dependency }
118
+ model.after_destroy lambda { |o| o.association(reflection.name).handle_dependency }
119
+ end
120
+
121
+ def self.define_validations(model, reflection)
122
+ if reflection.options.key?(:required)
123
+ reflection.options[:optional] = !reflection.options.delete(:required)
124
+ end
125
+
126
+ if reflection.options[:optional].nil?
127
+ required = model.belongs_to_required_by_default
128
+ else
129
+ required = !reflection.options[:optional]
130
+ end
131
+
132
+ super
133
+
134
+ if required
135
+ model.validates_presence_of reflection.name, message: :required
136
+ end
114
137
  end
115
138
  end
116
139
  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,15 +68,9 @@ 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
- if scope.arity > 0
86
- proc { |owner| instance_exec(owner, &scope).extending(mod) }
87
- else
88
- proc { instance_exec(&scope).extending(mod) }
89
- end
73
+ proc { |owner| instance_exec(owner, &scope).extending(mod) }
90
74
  else
91
75
  proc { extending(mod) }
92
76
  end
@@ -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
 
@@ -89,13 +89,13 @@ module ActiveRecord::Associations::Builder
89
89
 
90
90
  def middle_reflection(join_model)
91
91
  middle_name = [lhs_model.name.downcase.pluralize,
92
- association_name].join('_').gsub(/::/, '_').to_sym
92
+ association_name].join('_'.freeze).gsub('::'.freeze, '_'.freeze).to_sym
93
93
  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
94
+
95
+ HasMany.create_reflection(lhs_model,
96
+ middle_name,
97
+ nil,
98
+ middle_options)
99
99
  end
100
100
 
101
101
  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,10 +1,10 @@
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
7
+ def self.valid_options(options)
8
8
  valid = super + [:as, :foreign_type]
9
9
  valid += [:through, :source, :source_type] if options[:through]
10
10
  valid
@@ -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,8 +1,8 @@
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
5
+ def self.valid_options(options)
6
6
  super + [:dependent, :primary_key, :inverse_of, :required]
7
7
  end
8
8
 
@@ -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,19 @@ 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
+ replace(klass.find(ids).index_by(&:id).values_at(*ids))
79
76
  end
80
77
 
81
78
  def reset
@@ -172,6 +169,7 @@ module ActiveRecord
172
169
  # be chained. Since << flattens its argument list and inserts each record,
173
170
  # +push+ and +concat+ behave identically.
174
171
  def concat(*records)
172
+ records = records.flatten
175
173
  if owner.new_record?
176
174
  load_target
177
175
  concat_records(records)
@@ -239,11 +237,7 @@ module ActiveRecord
239
237
 
240
238
  # Count all records using SQL. Construct options and pass them with
241
239
  # 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
-
240
+ def count(column_name = nil)
247
241
  relation = scope
248
242
  if association_scope.distinct_value
249
243
  # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
@@ -275,7 +269,7 @@ module ActiveRecord
275
269
  _options = records.extract_options!
276
270
  dependent = _options[:dependent] || options[:dependent]
277
271
 
278
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
272
+ records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
279
273
  delete_or_destroy(records, dependent)
280
274
  end
281
275
 
@@ -286,7 +280,7 @@ module ActiveRecord
286
280
  # +:dependent+ option.
287
281
  def destroy(*records)
288
282
  return if records.empty?
289
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
283
+ records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
290
284
  delete_or_destroy(records, :destroy)
291
285
  end
292
286
 
@@ -310,7 +304,7 @@ module ActiveRecord
310
304
  elsif !loaded? && !association_scope.group_values.empty?
311
305
  load_target.size
312
306
  elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
313
- unsaved_records = target.select { |r| r.new_record? }
307
+ unsaved_records = target.select(&:new_record?)
314
308
  unsaved_records.size + count_records
315
309
  else
316
310
  count_records
@@ -343,7 +337,8 @@ module ActiveRecord
343
337
  end
344
338
 
345
339
  # Returns true if the collections is not empty.
346
- # Equivalent to +!collection.empty?+.
340
+ # If block given, loads all records and checks for one or more matches.
341
+ # Otherwise, equivalent to +!collection.empty?+.
347
342
  def any?
348
343
  if block_given?
349
344
  load_target.any? { |*block_args| yield(*block_args) }
@@ -353,7 +348,8 @@ module ActiveRecord
353
348
  end
354
349
 
355
350
  # Returns true if the collection has more than 1 record.
356
- # Equivalent to +collection.size > 1+.
351
+ # If block given, loads all records and checks for two or more matches.
352
+ # Otherwise, equivalent to +collection.size > 1+.
357
353
  def many?
358
354
  if block_given?
359
355
  load_target.many? { |*block_args| yield(*block_args) }
@@ -382,6 +378,8 @@ module ActiveRecord
382
378
  replace_common_records_in_memory(other_array, original_target)
383
379
  if other_array != original_target
384
380
  transaction { replace_records(other_array, original_target) }
381
+ else
382
+ other_array
385
383
  end
386
384
  end
387
385
  end
@@ -416,12 +414,16 @@ module ActiveRecord
416
414
 
417
415
  def replace_on_target(record, index, skip_callbacks)
418
416
  callback(:before_add, record) unless skip_callbacks
417
+
418
+ was_loaded = loaded?
419
419
  yield(record) if block_given?
420
420
 
421
- if index
422
- @target[index] = record
423
- else
424
- @target << record
421
+ unless !was_loaded && loaded?
422
+ if index
423
+ @target[index] = record
424
+ else
425
+ @target << record
426
+ end
425
427
  end
426
428
 
427
429
  callback(:after_add, record) unless skip_callbacks
@@ -521,7 +523,7 @@ module ActiveRecord
521
523
  def delete_or_destroy(records, method)
522
524
  records = records.flatten
523
525
  records.each { |record| raise_on_type_mismatch!(record) }
524
- existing_records = records.reject { |r| r.new_record? }
526
+ existing_records = records.reject(&:new_record?)
525
527
 
526
528
  if existing_records.empty?
527
529
  remove_records(existing_records, records, method)
@@ -568,7 +570,7 @@ module ActiveRecord
568
570
  def concat_records(records, should_raise = false)
569
571
  result = true
570
572
 
571
- records.flatten.each do |record|
573
+ records.each do |record|
572
574
  raise_on_type_mismatch!(record)
573
575
  add_to_target(record) do |rec|
574
576
  result &&= insert_record(rec, true, should_raise) unless owner.new_record?
@@ -612,13 +614,8 @@ module ActiveRecord
612
614
  if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
613
615
  assoc = owner.association(reflection.through_reflection.name)
614
616
  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
617
+ target_reflection = source.send(reflection.source_reflection.name)
618
+ target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
622
619
  } || target.include?(record)
623
620
  else
624
621
  target.include?(record)
@@ -629,7 +626,7 @@ module ActiveRecord
629
626
  # specified, then #find scans the entire collection.
630
627
  def find_by_scan(*args)
631
628
  expects_array = args.first.kind_of?(Array)
632
- ids = args.flatten.compact.map{ |arg| arg.to_s }.uniq
629
+ ids = args.flatten.compact.map(&:to_s).uniq
633
630
 
634
631
  if ids.size == 1
635
632
  id = ids.first