activerecord 5.0.7.2 → 5.1.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 (216) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +389 -2252
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +28 -28
  6. data/examples/simple.rb +3 -3
  7. data/lib/active_record.rb +20 -20
  8. data/lib/active_record/aggregations.rb +244 -244
  9. data/lib/active_record/association_relation.rb +5 -5
  10. data/lib/active_record/associations.rb +1579 -1569
  11. data/lib/active_record/associations/alias_tracker.rb +1 -1
  12. data/lib/active_record/associations/association.rb +23 -15
  13. data/lib/active_record/associations/association_scope.rb +83 -81
  14. data/lib/active_record/associations/belongs_to_association.rb +0 -1
  15. data/lib/active_record/associations/builder/belongs_to.rb +16 -14
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -2
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
  18. data/lib/active_record/associations/collection_association.rb +74 -241
  19. data/lib/active_record/associations/collection_proxy.rb +144 -70
  20. data/lib/active_record/associations/has_many_association.rb +15 -19
  21. data/lib/active_record/associations/has_many_through_association.rb +12 -5
  22. data/lib/active_record/associations/has_one_association.rb +22 -28
  23. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  24. data/lib/active_record/associations/join_dependency.rb +117 -115
  25. data/lib/active_record/associations/join_dependency/join_association.rb +16 -13
  26. data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  28. data/lib/active_record/associations/preloader.rb +94 -94
  29. data/lib/active_record/associations/preloader/association.rb +87 -64
  30. data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
  31. data/lib/active_record/associations/preloader/collection_association.rb +6 -6
  32. data/lib/active_record/associations/preloader/has_many.rb +0 -2
  33. data/lib/active_record/associations/preloader/singular_association.rb +6 -8
  34. data/lib/active_record/associations/preloader/through_association.rb +34 -41
  35. data/lib/active_record/associations/singular_association.rb +8 -25
  36. data/lib/active_record/associations/through_association.rb +3 -6
  37. data/lib/active_record/attribute.rb +98 -71
  38. data/lib/active_record/attribute/user_provided_default.rb +4 -2
  39. data/lib/active_record/attribute_assignment.rb +61 -61
  40. data/lib/active_record/attribute_decorators.rb +35 -13
  41. data/lib/active_record/attribute_methods.rb +56 -65
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
  43. data/lib/active_record/attribute_methods/dirty.rb +216 -34
  44. data/lib/active_record/attribute_methods/primary_key.rb +78 -73
  45. data/lib/active_record/attribute_methods/read.rb +39 -35
  46. data/lib/active_record/attribute_methods/serialization.rb +7 -7
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
  48. data/lib/active_record/attribute_methods/write.rb +36 -30
  49. data/lib/active_record/attribute_mutation_tracker.rb +53 -10
  50. data/lib/active_record/attribute_set.rb +9 -6
  51. data/lib/active_record/attribute_set/builder.rb +41 -49
  52. data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
  53. data/lib/active_record/attributes.rb +21 -21
  54. data/lib/active_record/autosave_association.rb +13 -13
  55. data/lib/active_record/base.rb +24 -22
  56. data/lib/active_record/callbacks.rb +52 -14
  57. data/lib/active_record/coders/yaml_column.rb +9 -11
  58. data/lib/active_record/collection_cache_key.rb +6 -17
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +320 -278
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -34
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -27
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -57
  64. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +9 -19
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +78 -79
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +99 -93
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -5
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +156 -128
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +424 -382
  71. data/lib/active_record/connection_adapters/column.rb +27 -5
  72. data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
  73. data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -43
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +49 -31
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +5 -6
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -26
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -28
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -35
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +9 -9
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  91. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
  93. data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
  94. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  95. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
  97. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +28 -30
  98. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
  100. data/lib/active_record/connection_adapters/postgresql/quoting.rb +38 -36
  101. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
  102. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
  103. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
  104. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +161 -170
  105. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +4 -4
  106. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -7
  107. data/lib/active_record/connection_adapters/postgresql_adapter.rb +179 -152
  108. data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
  109. data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
  110. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
  111. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -20
  112. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
  113. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
  114. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +187 -130
  116. data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
  117. data/lib/active_record/connection_handling.rb +14 -26
  118. data/lib/active_record/core.rb +110 -93
  119. data/lib/active_record/counter_cache.rb +62 -13
  120. data/lib/active_record/define_callbacks.rb +20 -0
  121. data/lib/active_record/dynamic_matchers.rb +80 -79
  122. data/lib/active_record/enum.rb +8 -6
  123. data/lib/active_record/errors.rb +58 -15
  124. data/lib/active_record/explain.rb +1 -2
  125. data/lib/active_record/explain_registry.rb +1 -1
  126. data/lib/active_record/explain_subscriber.rb +7 -4
  127. data/lib/active_record/fixture_set/file.rb +11 -8
  128. data/lib/active_record/fixtures.rb +66 -53
  129. data/lib/active_record/gem_version.rb +3 -3
  130. data/lib/active_record/inheritance.rb +93 -79
  131. data/lib/active_record/integration.rb +7 -7
  132. data/lib/active_record/internal_metadata.rb +3 -16
  133. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  134. data/lib/active_record/locking/optimistic.rb +64 -56
  135. data/lib/active_record/locking/pessimistic.rb +10 -1
  136. data/lib/active_record/log_subscriber.rb +29 -29
  137. data/lib/active_record/migration.rb +155 -172
  138. data/lib/active_record/migration/command_recorder.rb +94 -94
  139. data/lib/active_record/migration/compatibility.rb +76 -37
  140. data/lib/active_record/migration/join_table.rb +6 -6
  141. data/lib/active_record/model_schema.rb +85 -119
  142. data/lib/active_record/nested_attributes.rb +200 -199
  143. data/lib/active_record/null_relation.rb +10 -33
  144. data/lib/active_record/persistence.rb +45 -38
  145. data/lib/active_record/query_cache.rb +4 -8
  146. data/lib/active_record/querying.rb +2 -3
  147. data/lib/active_record/railtie.rb +16 -17
  148. data/lib/active_record/railties/controller_runtime.rb +6 -2
  149. data/lib/active_record/railties/databases.rake +125 -140
  150. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  151. data/lib/active_record/readonly_attributes.rb +2 -2
  152. data/lib/active_record/reflection.rb +79 -96
  153. data/lib/active_record/relation.rb +72 -115
  154. data/lib/active_record/relation/batches.rb +87 -58
  155. data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
  156. data/lib/active_record/relation/calculations.rb +154 -160
  157. data/lib/active_record/relation/delegation.rb +30 -29
  158. data/lib/active_record/relation/finder_methods.rb +195 -226
  159. data/lib/active_record/relation/merger.rb +58 -62
  160. data/lib/active_record/relation/predicate_builder.rb +92 -89
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
  165. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
  166. data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
  167. data/lib/active_record/relation/query_attribute.rb +1 -1
  168. data/lib/active_record/relation/query_methods.rb +247 -295
  169. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  170. data/lib/active_record/relation/spawn_methods.rb +4 -5
  171. data/lib/active_record/relation/where_clause.rb +79 -65
  172. data/lib/active_record/relation/where_clause_factory.rb +47 -8
  173. data/lib/active_record/result.rb +29 -31
  174. data/lib/active_record/runtime_registry.rb +3 -3
  175. data/lib/active_record/sanitization.rb +182 -197
  176. data/lib/active_record/schema.rb +3 -3
  177. data/lib/active_record/schema_dumper.rb +14 -37
  178. data/lib/active_record/schema_migration.rb +3 -3
  179. data/lib/active_record/scoping.rb +9 -10
  180. data/lib/active_record/scoping/default.rb +87 -91
  181. data/lib/active_record/scoping/named.rb +16 -28
  182. data/lib/active_record/secure_token.rb +2 -2
  183. data/lib/active_record/statement_cache.rb +13 -15
  184. data/lib/active_record/store.rb +31 -32
  185. data/lib/active_record/suppressor.rb +2 -1
  186. data/lib/active_record/table_metadata.rb +9 -5
  187. data/lib/active_record/tasks/database_tasks.rb +72 -65
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +75 -72
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +53 -48
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
  191. data/lib/active_record/timestamp.rb +39 -25
  192. data/lib/active_record/touch_later.rb +1 -2
  193. data/lib/active_record/transactions.rb +98 -110
  194. data/lib/active_record/type.rb +17 -13
  195. data/lib/active_record/type/adapter_specific_registry.rb +46 -42
  196. data/lib/active_record/type/decimal_without_scale.rb +9 -0
  197. data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
  198. data/lib/active_record/type/serialized.rb +8 -8
  199. data/lib/active_record/type/text.rb +9 -0
  200. data/lib/active_record/type/time.rb +0 -1
  201. data/lib/active_record/type/type_map.rb +11 -15
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type_caster.rb +2 -2
  204. data/lib/active_record/type_caster/connection.rb +8 -6
  205. data/lib/active_record/type_caster/map.rb +3 -1
  206. data/lib/active_record/validations.rb +4 -4
  207. data/lib/active_record/validations/associated.rb +1 -1
  208. data/lib/active_record/validations/presence.rb +2 -2
  209. data/lib/active_record/validations/uniqueness.rb +8 -39
  210. data/lib/active_record/version.rb +1 -1
  211. data/lib/rails/generators/active_record.rb +4 -4
  212. data/lib/rails/generators/active_record/migration.rb +2 -2
  213. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
  215. metadata +22 -13
  216. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -12,13 +12,21 @@ module ActiveRecord
12
12
  #
13
13
  # * +id+ - The id of the object you wish to reset a counter on.
14
14
  # * +counters+ - One or more association counters to reset. Association name or counter name can be given.
15
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
16
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
17
+ # touch that column or an array of symbols to touch just those ones.
15
18
  #
16
19
  # ==== Examples
17
20
  #
18
- # # For Post with id #1 records reset the comments_count
21
+ # # For the Post with id #1, reset the comments_count
19
22
  # Post.reset_counters(1, :comments)
20
- def reset_counters(id, *counters)
23
+ #
24
+ # # Like above, but also touch the +updated_at+ and/or +updated_on+
25
+ # # attributes.
26
+ # Post.reset_counters(1, :comments, touch: true)
27
+ def reset_counters(id, *counters, touch: nil)
21
28
  object = find(id)
29
+
22
30
  counters.each do |counter_association|
23
31
  has_many_association = _reflect_on_association(counter_association)
24
32
  unless has_many_association
@@ -26,7 +34,7 @@ module ActiveRecord
26
34
  has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
27
35
  counter_association = has_many_association.plural_name if has_many_association
28
36
  end
29
- raise ArgumentError, "'#{self.name}' has no association called '#{counter_association}'" unless has_many_association
37
+ raise ArgumentError, "'#{name}' has no association called '#{counter_association}'" unless has_many_association
30
38
 
31
39
  if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
32
40
  has_many_association = has_many_association.through_reflection
@@ -37,10 +45,12 @@ module ActiveRecord
37
45
  reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
38
46
  counter_name = reflection.counter_cache_column
39
47
 
40
- unscoped.where(primary_key => object.id).update_all(
41
- counter_name => object.send(counter_association).count(:all)
42
- )
48
+ updates = { counter_name.to_sym => object.send(counter_association).count(:all) }
49
+ updates.merge!(touch_updates(touch)) if touch
50
+
51
+ unscoped.where(primary_key => object.id).update_all(updates)
43
52
  end
53
+
44
54
  return true
45
55
  end
46
56
 
@@ -55,6 +65,9 @@ module ActiveRecord
55
65
  # * +id+ - The id of the object you wish to update a counter on or an array of ids.
56
66
  # * +counters+ - A Hash containing the names of the fields
57
67
  # to update as keys and the amount to update the field by as values.
68
+ # * <tt>:touch</tt> option - Touch timestamp columns when updating.
69
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
70
+ # touch that column or an array of symbols to touch just those ones.
58
71
  #
59
72
  # ==== Examples
60
73
  #
@@ -73,14 +86,30 @@ module ActiveRecord
73
86
  # # UPDATE posts
74
87
  # # SET comment_count = COALESCE(comment_count, 0) + 1
75
88
  # # WHERE id IN (10, 15)
89
+ #
90
+ # # For the Posts with id of 10 and 15, increment the comment_count by 1
91
+ # # and update the updated_at value for each counter.
92
+ # Post.update_counters [10, 15], comment_count: 1, touch: true
93
+ # # Executes the following SQL:
94
+ # # UPDATE posts
95
+ # # SET comment_count = COALESCE(comment_count, 0) + 1,
96
+ # # `updated_at` = '2016-10-13T09:59:23-05:00'
97
+ # # WHERE id IN (10, 15)
76
98
  def update_counters(id, counters)
99
+ touch = counters.delete(:touch)
100
+
77
101
  updates = counters.map do |counter_name, value|
78
- operator = value < 0 ? '-' : '+'
102
+ operator = value < 0 ? "-" : "+"
79
103
  quoted_column = connection.quote_column_name(counter_name)
80
104
  "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
81
105
  end
82
106
 
83
- unscoped.where(primary_key => id).update_all updates.join(', ')
107
+ if touch
108
+ touch_updates = touch_updates(touch)
109
+ updates << sanitize_sql_for_assignment(touch_updates) unless touch_updates.empty?
110
+ end
111
+
112
+ unscoped.where(primary_key => id).update_all updates.join(", ")
84
113
  end
85
114
 
86
115
  # Increment a numeric field by one, via a direct SQL update.
@@ -94,13 +123,20 @@ module ActiveRecord
94
123
  #
95
124
  # * +counter_name+ - The name of the field that should be incremented.
96
125
  # * +id+ - The id of the object that should be incremented or an array of ids.
126
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
127
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
128
+ # touch that column or an array of symbols to touch just those ones.
97
129
  #
98
130
  # ==== Examples
99
131
  #
100
132
  # # Increment the posts_count column for the record with an id of 5
101
133
  # DiscussionBoard.increment_counter(:posts_count, 5)
102
- def increment_counter(counter_name, id)
103
- update_counters(id, counter_name => 1)
134
+ #
135
+ # # Increment the posts_count column for the record with an id of 5
136
+ # # and update the updated_at value.
137
+ # DiscussionBoard.increment_counter(:posts_count, 5, touch: true)
138
+ def increment_counter(counter_name, id, touch: nil)
139
+ update_counters(id, counter_name => 1, touch: touch)
104
140
  end
105
141
 
106
142
  # Decrement a numeric field by one, via a direct SQL update.
@@ -112,14 +148,28 @@ module ActiveRecord
112
148
  #
113
149
  # * +counter_name+ - The name of the field that should be decremented.
114
150
  # * +id+ - The id of the object that should be decremented or an array of ids.
151
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
152
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
153
+ # touch that column or an array of symbols to touch just those ones.
115
154
  #
116
155
  # ==== Examples
117
156
  #
118
157
  # # Decrement the posts_count column for the record with an id of 5
119
158
  # DiscussionBoard.decrement_counter(:posts_count, 5)
120
- def decrement_counter(counter_name, id)
121
- update_counters(id, counter_name => -1)
159
+ #
160
+ # # Decrement the posts_count column for the record with an id of 5
161
+ # # and update the updated_at value.
162
+ # DiscussionBoard.decrement_counter(:posts_count, 5, touch: true)
163
+ def decrement_counter(counter_name, id, touch: nil)
164
+ update_counters(id, counter_name => -1, touch: touch)
122
165
  end
166
+
167
+ private
168
+ def touch_updates(touch)
169
+ touch = timestamp_attributes_for_update_in_model if touch == true
170
+ touch_time = current_time_from_proper_timezone
171
+ Array(touch).map { |column| [ column, touch_time ] }.to_h
172
+ end
123
173
  end
124
174
 
125
175
  private
@@ -159,6 +209,5 @@ module ActiveRecord
159
209
  yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
160
210
  end
161
211
  end
162
-
163
212
  end
164
213
  end
@@ -0,0 +1,20 @@
1
+ module ActiveRecord
2
+ # This module exists because `ActiveRecord::AttributeMethods::Dirty` needs to
3
+ # define callbacks, but continue to have its version of `save` be the super
4
+ # method of `ActiveRecord::Callbacks`. This will be removed when the removal
5
+ # of deprecated code removes this need.
6
+ module DefineCallbacks
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods # :nodoc:
10
+ include ActiveModel::Callbacks
11
+ end
12
+
13
+ included do
14
+ include ActiveModel::Validations::Callbacks
15
+
16
+ define_model_callbacks :initialize, :find, :touch, only: :after
17
+ define_model_callbacks :save, :create, :update, :destroy
18
+ end
19
+ end
20
+ end
@@ -1,6 +1,7 @@
1
+
1
2
  module ActiveRecord
2
3
  module DynamicMatchers #:nodoc:
3
- def respond_to?(name, include_private = false)
4
+ def respond_to_missing?(name, include_private = false)
4
5
  if self == Base
5
6
  super
6
7
  else
@@ -11,111 +12,111 @@ module ActiveRecord
11
12
 
12
13
  private
13
14
 
14
- def method_missing(name, *arguments, &block)
15
- match = Method.match(self, name)
15
+ def method_missing(name, *arguments, &block)
16
+ match = Method.match(self, name)
16
17
 
17
- if match && match.valid?
18
- match.define
19
- send(name, *arguments, &block)
20
- else
21
- super
18
+ if match && match.valid?
19
+ match.define
20
+ send(name, *arguments, &block)
21
+ else
22
+ super
23
+ end
22
24
  end
23
- end
24
25
 
25
- class Method
26
- @matchers = []
26
+ class Method
27
+ @matchers = []
27
28
 
28
- class << self
29
- attr_reader :matchers
29
+ class << self
30
+ attr_reader :matchers
30
31
 
31
- def match(model, name)
32
- klass = matchers.find { |k| name =~ k.pattern }
33
- klass.new(model, name) if klass
34
- end
32
+ def match(model, name)
33
+ klass = matchers.find { |k| k.pattern.match?(name) }
34
+ klass.new(model, name) if klass
35
+ end
35
36
 
36
- def pattern
37
- @pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
38
- end
37
+ def pattern
38
+ @pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
39
+ end
39
40
 
40
- def prefix
41
- raise NotImplementedError
42
- end
41
+ def prefix
42
+ raise NotImplementedError
43
+ end
43
44
 
44
- def suffix
45
- ''
45
+ def suffix
46
+ ""
47
+ end
46
48
  end
47
- end
48
49
 
49
- attr_reader :model, :name, :attribute_names
50
+ attr_reader :model, :name, :attribute_names
50
51
 
51
- def initialize(model, name)
52
- @model = model
53
- @name = name.to_s
54
- @attribute_names = @name.match(self.class.pattern)[1].split('_and_')
55
- @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
56
- end
52
+ def initialize(model, name)
53
+ @model = model
54
+ @name = name.to_s
55
+ @attribute_names = @name.match(self.class.pattern)[1].split("_and_")
56
+ @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
57
+ end
57
58
 
58
- def valid?
59
- attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
60
- end
59
+ def valid?
60
+ attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
61
+ end
61
62
 
62
- def define
63
- model.class_eval <<-CODE, __FILE__, __LINE__ + 1
64
- def self.#{name}(#{signature})
65
- #{body}
66
- end
67
- CODE
68
- end
63
+ def define
64
+ model.class_eval <<-CODE, __FILE__, __LINE__ + 1
65
+ def self.#{name}(#{signature})
66
+ #{body}
67
+ end
68
+ CODE
69
+ end
69
70
 
70
- private
71
+ private
71
72
 
72
- def body
73
- "#{finder}(#{attributes_hash})"
74
- end
73
+ def body
74
+ "#{finder}(#{attributes_hash})"
75
+ end
75
76
 
76
- # The parameters in the signature may have reserved Ruby words, in order
77
- # to prevent errors, we start each param name with `_`.
78
- def signature
79
- attribute_names.map { |name| "_#{name}" }.join(', ')
80
- end
77
+ # The parameters in the signature may have reserved Ruby words, in order
78
+ # to prevent errors, we start each param name with `_`.
79
+ def signature
80
+ attribute_names.map { |name| "_#{name}" }.join(", ")
81
+ end
81
82
 
82
- # Given that the parameters starts with `_`, the finder needs to use the
83
- # same parameter name.
84
- def attributes_hash
85
- "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(',') + "}"
86
- end
83
+ # Given that the parameters starts with `_`, the finder needs to use the
84
+ # same parameter name.
85
+ def attributes_hash
86
+ "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(",") + "}"
87
+ end
87
88
 
88
- def finder
89
- raise NotImplementedError
89
+ def finder
90
+ raise NotImplementedError
91
+ end
90
92
  end
91
- end
92
93
 
93
- class FindBy < Method
94
- Method.matchers << self
94
+ class FindBy < Method
95
+ Method.matchers << self
95
96
 
96
- def self.prefix
97
- "find_by"
98
- end
97
+ def self.prefix
98
+ "find_by"
99
+ end
99
100
 
100
- def finder
101
- "find_by"
101
+ def finder
102
+ "find_by"
103
+ end
102
104
  end
103
- end
104
105
 
105
- class FindByBang < Method
106
- Method.matchers << self
106
+ class FindByBang < Method
107
+ Method.matchers << self
107
108
 
108
- def self.prefix
109
- "find_by"
110
- end
109
+ def self.prefix
110
+ "find_by"
111
+ end
111
112
 
112
- def self.suffix
113
- "!"
114
- end
113
+ def self.suffix
114
+ "!"
115
+ end
115
116
 
116
- def finder
117
- "find_by!"
117
+ def finder
118
+ "find_by!"
119
+ end
118
120
  end
119
- end
120
121
  end
121
122
  end
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/object/deep_dup'
1
+ require "active_support/core_ext/object/deep_dup"
2
2
 
3
3
  module ActiveRecord
4
4
  # Declare an enum attribute where the values map to integers in the database,
@@ -140,9 +140,11 @@ module ActiveRecord
140
140
  end
141
141
  end
142
142
 
143
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
144
+ # Workaround for Ruby 2.2 "private attribute?" warning.
143
145
  protected
144
146
 
145
- attr_reader :name, :mapping, :subtype
147
+ attr_reader :name, :mapping, :subtype
146
148
  end
147
149
 
148
150
  def enum(definitions)
@@ -216,18 +218,18 @@ module ActiveRecord
216
218
 
217
219
  def detect_enum_conflict!(enum_name, method_name, klass_method = false)
218
220
  if klass_method && dangerous_class_method?(method_name)
219
- raise_conflict_error(enum_name, method_name, type: 'class')
221
+ raise_conflict_error(enum_name, method_name, type: "class")
220
222
  elsif !klass_method && dangerous_attribute_method?(method_name)
221
223
  raise_conflict_error(enum_name, method_name)
222
224
  elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
223
- raise_conflict_error(enum_name, method_name, source: 'another enum')
225
+ raise_conflict_error(enum_name, method_name, source: "another enum")
224
226
  end
225
227
  end
226
228
 
227
- def raise_conflict_error(enum_name, method_name, type: 'instance', source: 'Active Record')
229
+ def raise_conflict_error(enum_name, method_name, type: "instance", source: "Active Record")
228
230
  raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
229
231
  enum: enum_name,
230
- klass: self.name,
232
+ klass: name,
231
233
  type: type,
232
234
  method: method_name,
233
235
  source: source
@@ -1,5 +1,4 @@
1
1
  module ActiveRecord
2
-
3
2
  # = Active Record Errors
4
3
  #
5
4
  # Generic Active Record exception class.
@@ -44,7 +43,7 @@ module ActiveRecord
44
43
 
45
44
  # Raised when connection to the database could not been established (for example when
46
45
  # {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
47
- # is given a nil object).
46
+ # is given a +nil+ object).
48
47
  class ConnectionNotEstablished < ActiveRecordError
49
48
  end
50
49
 
@@ -96,20 +95,9 @@ module ActiveRecord
96
95
  #
97
96
  # Wraps the underlying database error as +cause+.
98
97
  class StatementInvalid < ActiveRecordError
99
-
100
- def initialize(message = nil, original_exception = nil)
101
- if original_exception
102
- ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
103
- "Exceptions will automatically capture the original exception.", caller)
104
- end
105
-
98
+ def initialize(message = nil)
106
99
  super(message || $!.try(:message))
107
100
  end
108
-
109
- def original_exception
110
- ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller)
111
- cause
112
- end
113
101
  end
114
102
 
115
103
  # Defunct wrapper class kept for compatibility.
@@ -125,10 +113,46 @@ module ActiveRecord
125
113
  class InvalidForeignKey < WrappedDatabaseException
126
114
  end
127
115
 
116
+ # Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
117
+ class MismatchedForeignKey < StatementInvalid
118
+ def initialize(adapter = nil, message: nil, table: nil, foreign_key: nil, target_table: nil, primary_key: nil)
119
+ @adapter = adapter
120
+ if table
121
+ msg = <<-EOM.strip_heredoc
122
+ Column `#{foreign_key}` on table `#{table}` has a type of `#{column_type(table, foreign_key)}`.
123
+ This does not match column `#{primary_key}` on `#{target_table}`, which has type `#{column_type(target_table, primary_key)}`.
124
+ To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :integer. (For example `t.integer #{foreign_key}`).
125
+ EOM
126
+ else
127
+ msg = <<-EOM
128
+ There is a mismatch between the foreign key and primary key column types.
129
+ Verify that the foreign key column type and the primary key of the associated table match types.
130
+ EOM
131
+ end
132
+ if message
133
+ msg << "\nOriginal message: #{message}"
134
+ end
135
+ super(msg)
136
+ end
137
+
138
+ private
139
+ def column_type(table, column)
140
+ @adapter.columns(table).detect { |c| c.name == column }.sql_type
141
+ end
142
+ end
143
+
144
+ # Raised when a record cannot be inserted or updated because it would violate a not null constraint.
145
+ class NotNullViolation < StatementInvalid
146
+ end
147
+
128
148
  # Raised when a record cannot be inserted or updated because a value too long for a column type.
129
149
  class ValueTooLong < StatementInvalid
130
150
  end
131
151
 
152
+ # Raised when values that executed are out of range.
153
+ class RangeError < StatementInvalid
154
+ end
155
+
132
156
  # Raised when number of bind variables in statement given to +:condition+ key
133
157
  # (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
134
158
  # does not match number of expected values supplied.
@@ -166,7 +190,6 @@ module ActiveRecord
166
190
  super("Stale object error.")
167
191
  end
168
192
  end
169
-
170
193
  end
171
194
 
172
195
  # Raised when association is being configured improperly or user tries to use
@@ -285,6 +308,26 @@ module ActiveRecord
285
308
  class TransactionIsolationError < ActiveRecordError
286
309
  end
287
310
 
311
+ # TransactionRollbackError will be raised when a transaction is rolled
312
+ # back by the database due to a serialization failure or a deadlock.
313
+ #
314
+ # See the following:
315
+ #
316
+ # * http://www.postgresql.org/docs/current/static/transaction-iso.html
317
+ # * https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_lock_deadlock
318
+ class TransactionRollbackError < StatementInvalid
319
+ end
320
+
321
+ # SerializationFailure will be raised when a transaction is rolled
322
+ # back by the database due to a serialization failure.
323
+ class SerializationFailure < TransactionRollbackError
324
+ end
325
+
326
+ # Deadlocked will be raised when a transaction is rolled
327
+ # back by the database when a deadlock is encountered.
328
+ class Deadlocked < TransactionRollbackError
329
+ end
330
+
288
331
  # IrreversibleOrderError is raised when a relation's order is too complex for
289
332
  # +reverse_order+ to automatically reverse.
290
333
  class IrreversibleOrderError < ActiveRecordError