activerecord 4.2.6 → 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 +4 -4
  2. data/CHANGELOG.md +1307 -1105
  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/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +3 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +11 -9
  11. data/lib/active_record/associations/association_scope.rb +73 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +50 -31
  21. data/lib/active_record/associations/collection_proxy.rb +69 -29
  22. data/lib/active_record/associations/foreign_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_association.rb +20 -71
  24. data/lib/active_record/associations/has_many_through_association.rb +8 -47
  25. data/lib/active_record/associations/has_one_association.rb +12 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +29 -19
  28. data/lib/active_record/associations/preloader/association.rb +46 -52
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +14 -4
  34. data/lib/active_record/associations/singular_association.rb +7 -1
  35. data/lib/active_record/associations/through_association.rb +11 -3
  36. data/lib/active_record/associations.rb +317 -209
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +68 -18
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +6 -4
  52. data/lib/active_record/attribute_set.rb +30 -3
  53. data/lib/active_record/attributes.rb +199 -80
  54. data/lib/active_record/autosave_association.rb +49 -16
  55. data/lib/active_record/base.rb +32 -23
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -140
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -59
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -362
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -176
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  100. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  102. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  105. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  106. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  107. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  108. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
  110. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  111. data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
  112. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  113. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  114. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  115. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  117. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +148 -203
  118. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  119. data/lib/active_record/connection_handling.rb +37 -14
  120. data/lib/active_record/core.rb +89 -107
  121. data/lib/active_record/counter_cache.rb +13 -24
  122. data/lib/active_record/dynamic_matchers.rb +1 -20
  123. data/lib/active_record/enum.rb +113 -76
  124. data/lib/active_record/errors.rb +87 -48
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +26 -5
  128. data/lib/active_record/fixtures.rb +76 -40
  129. data/lib/active_record/gem_version.rb +3 -3
  130. data/lib/active_record/inheritance.rb +32 -40
  131. data/lib/active_record/integration.rb +4 -4
  132. data/lib/active_record/internal_metadata.rb +56 -0
  133. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  134. data/lib/active_record/locale/en.yml +3 -2
  135. data/lib/active_record/locking/optimistic.rb +15 -15
  136. data/lib/active_record/locking/pessimistic.rb +1 -1
  137. data/lib/active_record/log_subscriber.rb +43 -21
  138. data/lib/active_record/migration/command_recorder.rb +59 -18
  139. data/lib/active_record/migration/compatibility.rb +126 -0
  140. data/lib/active_record/migration.rb +364 -109
  141. data/lib/active_record/model_schema.rb +128 -38
  142. data/lib/active_record/nested_attributes.rb +58 -29
  143. data/lib/active_record/null_relation.rb +16 -8
  144. data/lib/active_record/persistence.rb +121 -80
  145. data/lib/active_record/query_cache.rb +15 -18
  146. data/lib/active_record/querying.rb +10 -9
  147. data/lib/active_record/railtie.rb +27 -18
  148. data/lib/active_record/railties/controller_runtime.rb +1 -1
  149. data/lib/active_record/railties/databases.rake +58 -45
  150. data/lib/active_record/readonly_attributes.rb +1 -1
  151. data/lib/active_record/reflection.rb +282 -115
  152. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  153. data/lib/active_record/relation/batches.rb +139 -34
  154. data/lib/active_record/relation/calculations.rb +80 -102
  155. data/lib/active_record/relation/delegation.rb +7 -20
  156. data/lib/active_record/relation/finder_methods.rb +163 -81
  157. data/lib/active_record/relation/from_clause.rb +32 -0
  158. data/lib/active_record/relation/merger.rb +16 -42
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -15
  160. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  163. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  165. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  166. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  167. data/lib/active_record/relation/predicate_builder.rb +120 -107
  168. data/lib/active_record/relation/query_attribute.rb +19 -0
  169. data/lib/active_record/relation/query_methods.rb +308 -244
  170. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  171. data/lib/active_record/relation/spawn_methods.rb +4 -7
  172. data/lib/active_record/relation/where_clause.rb +174 -0
  173. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  174. data/lib/active_record/relation.rb +176 -116
  175. data/lib/active_record/result.rb +4 -3
  176. data/lib/active_record/runtime_registry.rb +1 -1
  177. data/lib/active_record/sanitization.rb +95 -66
  178. data/lib/active_record/schema.rb +26 -22
  179. data/lib/active_record/schema_dumper.rb +62 -38
  180. data/lib/active_record/schema_migration.rb +11 -17
  181. data/lib/active_record/scoping/default.rb +23 -9
  182. data/lib/active_record/scoping/named.rb +49 -28
  183. data/lib/active_record/scoping.rb +32 -15
  184. data/lib/active_record/secure_token.rb +38 -0
  185. data/lib/active_record/serialization.rb +2 -4
  186. data/lib/active_record/statement_cache.rb +16 -14
  187. data/lib/active_record/store.rb +8 -3
  188. data/lib/active_record/suppressor.rb +58 -0
  189. data/lib/active_record/table_metadata.rb +68 -0
  190. data/lib/active_record/tasks/database_tasks.rb +58 -41
  191. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -20
  192. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
  193. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  194. data/lib/active_record/timestamp.rb +20 -9
  195. data/lib/active_record/touch_later.rb +58 -0
  196. data/lib/active_record/transactions.rb +138 -56
  197. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  198. data/lib/active_record/type/date.rb +2 -41
  199. data/lib/active_record/type/date_time.rb +2 -49
  200. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  201. data/lib/active_record/type/internal/timezone.rb +15 -0
  202. data/lib/active_record/type/serialized.rb +15 -14
  203. data/lib/active_record/type/time.rb +10 -16
  204. data/lib/active_record/type/type_map.rb +4 -4
  205. data/lib/active_record/type.rb +66 -17
  206. data/lib/active_record/type_caster/connection.rb +29 -0
  207. data/lib/active_record/type_caster/map.rb +19 -0
  208. data/lib/active_record/type_caster.rb +7 -0
  209. data/lib/active_record/validations/absence.rb +23 -0
  210. data/lib/active_record/validations/associated.rb +10 -3
  211. data/lib/active_record/validations/length.rb +24 -0
  212. data/lib/active_record/validations/presence.rb +11 -12
  213. data/lib/active_record/validations/uniqueness.rb +30 -29
  214. data/lib/active_record/validations.rb +33 -32
  215. data/lib/active_record.rb +7 -2
  216. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  217. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  218. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  219. data/lib/rails/generators/active_record/migration.rb +7 -0
  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 +58 -34
  224. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  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 -50
  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 -105
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  def replace(record)
11
11
  if record
12
12
  raise_on_type_mismatch!(record)
13
- update_counters(record)
13
+ update_counters_on_replace(record)
14
14
  replace_keys(record)
15
15
  set_inverse_instance(record)
16
16
  @updated = true
@@ -32,45 +32,38 @@ module ActiveRecord
32
32
  end
33
33
 
34
34
  def decrement_counters # :nodoc:
35
- with_cache_name { |name| decrement_counter name }
35
+ update_counters(-1)
36
36
  end
37
37
 
38
38
  def increment_counters # :nodoc:
39
- with_cache_name { |name| increment_counter name }
39
+ update_counters(1)
40
40
  end
41
41
 
42
42
  private
43
43
 
44
- def find_target?
45
- !loaded? && foreign_key_present? && klass
46
- end
47
-
48
- def with_cache_name
49
- counter_cache_name = reflection.counter_cache_column
50
- return unless counter_cache_name && owner.persisted?
51
- yield counter_cache_name
44
+ def update_counters(by)
45
+ if require_counter_update? && foreign_key_present?
46
+ if target && !stale_target?
47
+ target.increment!(reflection.counter_cache_column, by)
48
+ else
49
+ klass.update_counters(target_id, reflection.counter_cache_column => by)
50
+ end
51
+ end
52
52
  end
53
53
 
54
- def update_counters(record)
55
- with_cache_name do |name|
56
- return unless different_target? record
57
- record.class.increment_counter(name, record.id)
58
- decrement_counter name
59
- end
54
+ def find_target?
55
+ !loaded? && foreign_key_present? && klass
60
56
  end
61
57
 
62
- def decrement_counter(counter_cache_name)
63
- if foreign_key_present?
64
- klass.decrement_counter(counter_cache_name, target_id)
65
- end
58
+ def require_counter_update?
59
+ reflection.counter_cache_column && owner.persisted?
66
60
  end
67
61
 
68
- def increment_counter(counter_cache_name)
69
- if foreign_key_present?
70
- klass.increment_counter(counter_cache_name, target_id)
71
- if target && !stale_target? && counter_cache_available_in_memory?(counter_cache_name)
72
- target.increment(counter_cache_name)
73
- end
62
+ def update_counters_on_replace(record)
63
+ if require_counter_update? && different_target?(record)
64
+ owner.instance_variable_set :@_after_replace_counter_called, true
65
+ record.increment!(reflection.counter_cache_column)
66
+ decrement_counters
74
67
  end
75
68
  end
76
69
 
@@ -107,13 +100,9 @@ module ActiveRecord
107
100
  end
108
101
 
109
102
  def stale_state
110
- result = owner._read_attribute(reflection.foreign_key)
103
+ result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
111
104
  result && result.to_s
112
105
  end
113
-
114
- def counter_cache_available_in_memory?(counter_cache_name)
115
- target.respond_to?(counter_cache_name)
116
- end
117
106
  end
118
107
  end
119
108
  end
@@ -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