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
@@ -5,15 +5,36 @@ module ActiveRecord
5
5
  # knows how to invert the following commands:
6
6
  #
7
7
  # * add_column
8
+ # * add_foreign_key
8
9
  # * add_index
10
+ # * add_reference
9
11
  # * add_timestamps
10
- # * create_table
12
+ # * change_column
13
+ # * change_column_default (must supply a :from and :to option)
14
+ # * change_column_null
11
15
  # * create_join_table
16
+ # * create_table
17
+ # * disable_extension
18
+ # * drop_join_table
19
+ # * drop_table (must supply a block)
20
+ # * enable_extension
21
+ # * remove_column (must supply a type)
22
+ # * remove_columns (must specify at least one column name or more)
23
+ # * remove_foreign_key (must supply a second table)
24
+ # * remove_index
25
+ # * remove_reference
12
26
  # * remove_timestamps
13
27
  # * rename_column
14
28
  # * rename_index
15
29
  # * rename_table
16
30
  class CommandRecorder
31
+ ReversibleAndIrreversibleMethods = [:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
32
+ :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
33
+ :change_column_default, :add_reference, :remove_reference, :transaction,
34
+ :drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension,
35
+ :change_column, :execute, :remove_columns, :change_column_null,
36
+ :add_foreign_key, :remove_foreign_key
37
+ ]
17
38
  include JoinTable
18
39
 
19
40
  attr_accessor :commands, :delegate, :reverting
@@ -41,7 +62,7 @@ module ActiveRecord
41
62
  @reverting = !@reverting
42
63
  end
43
64
 
44
- # record +command+. +command+ should be a method name and arguments.
65
+ # Record +command+. +command+ should be a method name and arguments.
45
66
  # For example:
46
67
  #
47
68
  # recorder.record(:method_name, [:arg1, :arg2])
@@ -62,7 +83,12 @@ module ActiveRecord
62
83
  # invert the +command+.
63
84
  def inverse_of(command, args, &block)
64
85
  method = :"invert_#{command}"
65
- raise IrreversibleMigration unless respond_to?(method, true)
86
+ raise IrreversibleMigration, <<-MSG.strip_heredoc unless respond_to?(method, true)
87
+ This migration uses #{command}, which is not automatically reversible.
88
+ To make the migration reversible you can either:
89
+ 1. Define #up and #down methods in place of the #change method.
90
+ 2. Use the #reversible method to define reversible behavior.
91
+ MSG
66
92
  send(method, args, &block)
67
93
  end
68
94
 
@@ -70,14 +96,7 @@ module ActiveRecord
70
96
  super || delegate.respond_to?(*args)
71
97
  end
72
98
 
73
- [:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
74
- :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
75
- :change_column_default, :add_reference, :remove_reference, :transaction,
76
- :drop_join_table, :drop_table, :execute_block, :enable_extension,
77
- :change_column, :execute, :remove_columns, :change_column_null,
78
- :add_foreign_key, :remove_foreign_key
79
- # irreversible methods need to be here too
80
- ].each do |method|
99
+ ReversibleAndIrreversibleMethods.each do |method|
81
100
  class_eval <<-EOV, __FILE__, __LINE__ + 1
82
101
  def #{method}(*args, &block) # def create_table(*args, &block)
83
102
  record(:"#{method}", args, &block) # record(:create_table, args, &block)
@@ -151,19 +170,31 @@ module ActiveRecord
151
170
  end
152
171
 
153
172
  def invert_remove_index(args)
154
- table, options = *args
155
-
156
- unless options && options.is_a?(Hash) && options[:column]
157
- raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
173
+ table, options_or_column = *args
174
+ if (options = options_or_column).is_a?(Hash)
175
+ unless options[:column]
176
+ raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
177
+ end
178
+ options = options.dup
179
+ [:add_index, [table, options.delete(:column), options]]
180
+ elsif (column = options_or_column).present?
181
+ [:add_index, [table, column]]
158
182
  end
159
-
160
- options = options.dup
161
- [:add_index, [table, options.delete(:column), options]]
162
183
  end
163
184
 
164
185
  alias :invert_add_belongs_to :invert_add_reference
165
186
  alias :invert_remove_belongs_to :invert_remove_reference
166
187
 
188
+ def invert_change_column_default(args)
189
+ table, column, options = *args
190
+
191
+ unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
192
+ raise ActiveRecord::IrreversibleMigration, "change_column_default is only reversible if given a :from and :to option."
193
+ end
194
+
195
+ [:change_column_default, [table, column, from: options[:to], to: options[:from]]]
196
+ end
197
+
167
198
  def invert_change_column_null(args)
168
199
  args[2] = !args[2]
169
200
  [:change_column_null, args]
@@ -184,6 +215,16 @@ module ActiveRecord
184
215
  [:remove_foreign_key, [from_table, options]]
185
216
  end
186
217
 
218
+ def invert_remove_foreign_key(args)
219
+ from_table, to_table, remove_options = args
220
+ raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil? || to_table.is_a?(Hash)
221
+
222
+ reversed_args = [from_table, to_table]
223
+ reversed_args << remove_options if remove_options
224
+
225
+ [:add_foreign_key, reversed_args]
226
+ end
227
+
187
228
  # Forwards any missing method call to the \target.
188
229
  def method_missing(method, *args, &block)
189
230
  if @delegate.respond_to?(method)
@@ -0,0 +1,90 @@
1
+ module ActiveRecord
2
+ class Migration
3
+ module Compatibility # :nodoc: all
4
+ V5_0 = Current
5
+
6
+ module FourTwoShared
7
+ module TableDefinition
8
+ def timestamps(*, **options)
9
+ options[:null] = true if options[:null].nil?
10
+ super
11
+ end
12
+ end
13
+
14
+ def create_table(table_name, options = {})
15
+ if block_given?
16
+ super(table_name, options) do |t|
17
+ class << t
18
+ prepend TableDefinition
19
+ end
20
+ yield t
21
+ end
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def add_timestamps(*, **options)
28
+ options[:null] = true if options[:null].nil?
29
+ super
30
+ end
31
+
32
+ def index_exists?(table_name, column_name, options = {})
33
+ column_names = Array(column_name).map(&:to_s)
34
+ options[:name] =
35
+ if options.key?(:name).present?
36
+ options[:name].to_s
37
+ else
38
+ index_name(table_name, column: column_names)
39
+ end
40
+ super
41
+ end
42
+
43
+ def remove_index(table_name, options = {})
44
+ index_name = index_name_for_remove(table_name, options)
45
+ execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
46
+ end
47
+
48
+ private
49
+
50
+ def index_name_for_remove(table_name, options = {})
51
+ index_name = index_name(table_name, options)
52
+
53
+ unless index_name_exists?(table_name, index_name, true)
54
+ if options.is_a?(Hash) && options.has_key?(:name)
55
+ options_without_column = options.dup
56
+ options_without_column.delete :column
57
+ index_name_without_column = index_name(table_name, options_without_column)
58
+
59
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
60
+ end
61
+
62
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
63
+ end
64
+
65
+ index_name
66
+ end
67
+ end
68
+
69
+ class V4_2 < V5_0
70
+ # 4.2 is defined as a module because it needs to be shared with
71
+ # Legacy. When the time comes, V5_0 should be defined straight
72
+ # in its class.
73
+ include FourTwoShared
74
+ end
75
+
76
+ module Legacy
77
+ include FourTwoShared
78
+
79
+ def run(*)
80
+ ActiveSupport::Deprecation.warn \
81
+ "Directly inheriting from ActiveRecord::Migration is deprecated. " \
82
+ "Please specify the Rails release the migration was written for:\n" \
83
+ "\n" \
84
+ " class #{self.class.name} < ActiveRecord::Migration[4.2]"
85
+ super
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -50,6 +50,13 @@ module ActiveRecord
50
50
  class_attribute :pluralize_table_names, instance_writer: false
51
51
  self.pluralize_table_names = true
52
52
 
53
+ ##
54
+ # :singleton-method:
55
+ # Accessor for the list of columns names the model should ignore. Ignored columns won't have attribute
56
+ # accessors defined, and won't be referenced in SQL queries.
57
+ class_attribute :ignored_columns, instance_accessor: false
58
+ self.ignored_columns = [].freeze
59
+
53
60
  self.inheritance_column = 'type'
54
61
 
55
62
  delegate :type_for_attribute, to: :class
@@ -111,17 +118,6 @@ module ActiveRecord
111
118
  # class Mouse < ActiveRecord::Base
112
119
  # self.table_name = "mice"
113
120
  # end
114
- #
115
- # Alternatively, you can override the table_name method to define your
116
- # own computation. (Possibly using <tt>super</tt> to manipulate the default
117
- # table name.) Example:
118
- #
119
- # class Post < ActiveRecord::Base
120
- # def self.table_name
121
- # "special_" + super
122
- # end
123
- # end
124
- # Post.table_name # => "special_posts"
125
121
  def table_name
126
122
  reset_table_name unless defined?(@table_name)
127
123
  @table_name
@@ -132,9 +128,6 @@ module ActiveRecord
132
128
  # class Project < ActiveRecord::Base
133
129
  # self.table_name = "project"
134
130
  # end
135
- #
136
- # You can also just define your own <tt>self.table_name</tt> method; see
137
- # the documentation for ActiveRecord::Base#table_name.
138
131
  def table_name=(value)
139
132
  value = value && value.to_s
140
133
 
@@ -147,7 +140,7 @@ module ActiveRecord
147
140
  @quoted_table_name = nil
148
141
  @arel_table = nil
149
142
  @sequence_name = nil unless defined?(@explicit_sequence_name) && @explicit_sequence_name
150
- @relation = Relation.create(self, arel_table)
143
+ @predicate_builder = nil
151
144
  end
152
145
 
153
146
  # Returns a quoted version of the table name, used to construct SQL statements.
@@ -227,37 +220,46 @@ module ActiveRecord
227
220
 
228
221
  # Indicates whether the table associated with this class exists
229
222
  def table_exists?
230
- connection.schema_cache.table_exists?(table_name)
223
+ connection.schema_cache.data_source_exists?(table_name)
231
224
  end
232
225
 
233
226
  def attributes_builder # :nodoc:
234
- @attributes_builder ||= AttributeSet::Builder.new(column_types, primary_key)
227
+ @attributes_builder ||= AttributeSet::Builder.new(attribute_types, primary_key)
235
228
  end
236
229
 
237
- def column_types # :nodoc:
238
- @column_types ||= columns_hash.transform_values(&:cast_type).tap do |h|
239
- h.default = Type::Value.new
240
- end
230
+ def columns_hash # :nodoc:
231
+ load_schema
232
+ @columns_hash
233
+ end
234
+
235
+ def columns
236
+ load_schema
237
+ @columns ||= columns_hash.values
238
+ end
239
+
240
+ def attribute_types # :nodoc:
241
+ load_schema
242
+ @attribute_types ||= Hash.new(Type::Value.new)
241
243
  end
242
244
 
243
245
  def type_for_attribute(attr_name) # :nodoc:
244
- column_types[attr_name]
246
+ attribute_types[attr_name]
245
247
  end
246
248
 
247
249
  # Returns a hash where the keys are column names and the values are
248
- # default values when instantiating the AR object for this table.
250
+ # default values when instantiating the Active Record object for this table.
249
251
  def column_defaults
250
- _default_attributes.dup.to_hash
252
+ load_schema
253
+ _default_attributes.to_hash
251
254
  end
252
255
 
253
256
  def _default_attributes # :nodoc:
254
- @default_attributes ||= attributes_builder.build_from_database(
255
- raw_default_values)
257
+ @default_attributes ||= AttributeSet.new({})
256
258
  end
257
259
 
258
260
  # Returns an array of column names as strings.
259
261
  def column_names
260
- @column_names ||= columns.map { |column| column.name }
262
+ @column_names ||= columns.map(&:name)
261
263
  end
262
264
 
263
265
  # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
@@ -273,7 +275,7 @@ module ActiveRecord
273
275
  # when just after creating a table you want to populate it with some default
274
276
  # values, eg:
275
277
  #
276
- # class CreateJobLevels < ActiveRecord::Migration
278
+ # class CreateJobLevels < ActiveRecord::Migration[5.0]
277
279
  # def up
278
280
  # create_table :job_levels do |t|
279
281
  # t.integer :id
@@ -295,21 +297,53 @@ module ActiveRecord
295
297
  def reset_column_information
296
298
  connection.clear_cache!
297
299
  undefine_attribute_methods
298
- connection.schema_cache.clear_table_cache!(table_name)
300
+ connection.schema_cache.clear_data_source_cache!(table_name)
299
301
 
300
- @arel_engine = nil
301
- @column_names = nil
302
- @column_types = nil
303
- @content_columns = nil
304
- @default_attributes = nil
305
- @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
306
- @relation = nil
307
-
308
- initialize_find_by_cache
302
+ reload_schema_from_cache
309
303
  end
310
304
 
311
305
  private
312
306
 
307
+ def schema_loaded?
308
+ defined?(@columns_hash) && @columns_hash
309
+ end
310
+
311
+ def load_schema
312
+ unless schema_loaded?
313
+ load_schema!
314
+ end
315
+ end
316
+
317
+ def load_schema!
318
+ @columns_hash = connection.schema_cache.columns_hash(table_name).except(*ignored_columns)
319
+ @columns_hash.each do |name, column|
320
+ warn_if_deprecated_type(column)
321
+ define_attribute(
322
+ name,
323
+ connection.lookup_cast_type_from_column(column),
324
+ default: column.default,
325
+ user_provided_default: false
326
+ )
327
+ end
328
+ end
329
+
330
+ def reload_schema_from_cache
331
+ @arel_engine = nil
332
+ @arel_table = nil
333
+ @column_names = nil
334
+ @attribute_types = nil
335
+ @content_columns = nil
336
+ @default_attributes = nil
337
+ @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
338
+ @attributes_builder = nil
339
+ @columns = nil
340
+ @columns_hash = nil
341
+ @attribute_names = nil
342
+ direct_descendants.each do |descendant|
343
+ descendant.send(:reload_schema_from_cache)
344
+ end
345
+ end
346
+
313
347
  # Guesses the table name, but does not decorate it with prefix and suffix information.
314
348
  def undecorated_table_name(class_name = base_class.name)
315
349
  table_name = class_name.to_s.demodulize.underscore
@@ -334,8 +368,26 @@ module ActiveRecord
334
368
  end
335
369
  end
336
370
 
337
- def raw_default_values
338
- columns_hash.transform_values(&:default)
371
+ def warn_if_deprecated_type(column)
372
+ return if attributes_to_define_after_schema_loads.key?(column.name)
373
+ if column.respond_to?(:oid) && column.sql_type.start_with?("point")
374
+ if column.array?
375
+ array_arguments = ", array: true"
376
+ else
377
+ array_arguments = ""
378
+ end
379
+ ActiveSupport::Deprecation.warn(<<-WARNING.strip_heredoc)
380
+ The behavior of the `:point` type will be changing in Rails 5.1 to
381
+ return a `Point` object, rather than an `Array`. If you'd like to
382
+ keep the old behavior, you can add this line to #{self.name}:
383
+
384
+ attribute :#{column.name}, :legacy_point#{array_arguments}
385
+
386
+ If you'd like the new behavior today, you can add this line:
387
+
388
+ attribute :#{column.name}, :point#{array_arguments}
389
+ WARNING
390
+ end
339
391
  end
340
392
  end
341
393
  end
@@ -81,6 +81,9 @@ module ActiveRecord
81
81
  #
82
82
  # Note that the model will _not_ be destroyed until the parent is saved.
83
83
  #
84
+ # Also note that the model will not be destroyed unless you also specify
85
+ # its id in the updated hash.
86
+ #
84
87
  # === One-to-many
85
88
  #
86
89
  # Consider a member that has a number of posts:
@@ -111,7 +114,7 @@ module ActiveRecord
111
114
  # member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
112
115
  # member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
113
116
  #
114
- # You may also set a :reject_if proc to silently ignore any new record
117
+ # You may also set a +:reject_if+ proc to silently ignore any new record
115
118
  # hashes if they fail to pass your criteria. For example, the previous
116
119
  # example could be rewritten as:
117
120
  #
@@ -133,7 +136,7 @@ module ActiveRecord
133
136
  # member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
134
137
  # member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
135
138
  #
136
- # Alternatively, :reject_if also accepts a symbol for using methods:
139
+ # Alternatively, +:reject_if+ also accepts a symbol for using methods:
137
140
  #
138
141
  # class Member < ActiveRecord::Base
139
142
  # has_many :posts
@@ -144,8 +147,8 @@ module ActiveRecord
144
147
  # has_many :posts
145
148
  # accepts_nested_attributes_for :posts, reject_if: :reject_posts
146
149
  #
147
- # def reject_posts(attributed)
148
- # attributed['title'].blank?
150
+ # def reject_posts(attributes)
151
+ # attributes['title'].blank?
149
152
  # end
150
153
  # end
151
154
  #
@@ -163,6 +166,11 @@ module ActiveRecord
163
166
  # member.posts.first.title # => '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!'
164
167
  # member.posts.second.title # => '[UPDATED] other post'
165
168
  #
169
+ # However, the above applies if the parent model is being updated as well.
170
+ # For example, If you wanted to create a +member+ named _joe_ and wanted to
171
+ # update the +posts+ at the same time, that would give an
172
+ # ActiveRecord::RecordNotFound error.
173
+ #
166
174
  # By default the associated records are protected from being destroyed. If
167
175
  # you want to destroy any of the associated records through the attributes
168
176
  # hash, you have to enable it first using the <tt>:allow_destroy</tt>
@@ -205,20 +213,20 @@ module ActiveRecord
205
213
  #
206
214
  # Passing attributes for an associated collection in the form of a hash
207
215
  # of hashes can be used with hashes generated from HTTP/HTML parameters,
208
- # where there maybe no natural way to submit an array of hashes.
216
+ # where there may be no natural way to submit an array of hashes.
209
217
  #
210
218
  # === Saving
211
219
  #
212
220
  # All changes to models, including the destruction of those marked for
213
221
  # destruction, are saved and destroyed automatically and atomically when
214
222
  # the parent model is saved. This happens inside the transaction initiated
215
- # by the parents save method. See ActiveRecord::AutosaveAssociation.
223
+ # by the parent's save method. See ActiveRecord::AutosaveAssociation.
216
224
  #
217
225
  # === Validating the presence of a parent model
218
226
  #
219
227
  # If you want to validate that a child record is associated with a parent
220
- # record, you can use <tt>validates_presence_of</tt> and
221
- # <tt>inverse_of</tt> as this example illustrates:
228
+ # record, you can use the +validates_presence_of+ method and the +:inverse_of+
229
+ # key as this example illustrates:
222
230
  #
223
231
  # class Member < ActiveRecord::Base
224
232
  # has_many :posts, inverse_of: :member
@@ -230,7 +238,7 @@ module ActiveRecord
230
238
  # validates_presence_of :member
231
239
  # end
232
240
  #
233
- # Note that if you do not specify the <tt>inverse_of</tt> option, then
241
+ # Note that if you do not specify the +:inverse_of+ option, then
234
242
  # Active Record will try to automatically guess the inverse association
235
243
  # based on heuristics.
236
244
  #
@@ -264,29 +272,31 @@ module ActiveRecord
264
272
  # Allows you to specify a Proc or a Symbol pointing to a method
265
273
  # that checks whether a record should be built for a certain attribute
266
274
  # hash. The hash is passed to the supplied Proc or the method
267
- # and it should return either +true+ or +false+. When no :reject_if
275
+ # and it should return either +true+ or +false+. When no +:reject_if+
268
276
  # is specified, a record will be built for all attribute hashes that
269
277
  # do not have a <tt>_destroy</tt> value that evaluates to true.
270
278
  # Passing <tt>:all_blank</tt> instead of a Proc will create a proc
271
279
  # that will reject a record where all the attributes are blank excluding
272
- # any value for _destroy.
280
+ # any value for +_destroy+.
273
281
  # [:limit]
274
- # Allows you to specify the maximum number of the associated records that
275
- # can be processed with the nested attributes. Limit also can be specified as a
276
- # Proc or a Symbol pointing to a method that should return number. If the size of the
277
- # nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
278
- # exception is raised. If omitted, any number associations can be processed.
279
- # Note that the :limit option is only applicable to one-to-many associations.
282
+ # Allows you to specify the maximum number of associated records that
283
+ # can be processed with the nested attributes. Limit also can be specified
284
+ # as a Proc or a Symbol pointing to a method that should return a number.
285
+ # If the size of the nested attributes array exceeds the specified limit,
286
+ # NestedAttributes::TooManyRecords exception is raised. If omitted, any
287
+ # number of associations can be processed.
288
+ # Note that the +:limit+ option is only applicable to one-to-many
289
+ # associations.
280
290
  # [:update_only]
281
291
  # For a one-to-one association, this option allows you to specify how
282
- # nested attributes are to be used when an associated record already
292
+ # nested attributes are going to be used when an associated record already
283
293
  # exists. In general, an existing record may either be updated with the
284
294
  # new set of attribute values or be replaced by a wholly new record
285
- # containing those values. By default the :update_only option is +false+
295
+ # containing those values. By default the +:update_only+ option is +false+
286
296
  # and the nested attributes are used to update the existing record only
287
297
  # if they include the record's <tt>:id</tt> value. Otherwise a new
288
298
  # record will be instantiated and used to replace the existing one.
289
- # However if the :update_only option is +true+, the nested attributes
299
+ # However if the +:update_only+ option is +true+, the nested attributes
290
300
  # are used to update the record's attributes always, regardless of
291
301
  # whether the <tt>:id</tt> is present. The option is ignored for collection
292
302
  # associations.
@@ -376,6 +386,9 @@ module ActiveRecord
376
386
  # then the existing record will be marked for destruction.
377
387
  def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
378
388
  options = self.nested_attributes_options[association_name]
389
+ if attributes.respond_to?(:permitted?)
390
+ attributes = attributes.to_h
391
+ end
379
392
  attributes = attributes.with_indifferent_access
380
393
  existing_record = send(association_name)
381
394
 
@@ -432,6 +445,9 @@ module ActiveRecord
432
445
  # ])
433
446
  def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
434
447
  options = self.nested_attributes_options[association_name]
448
+ if attributes_collection.respond_to?(:permitted?)
449
+ attributes_collection = attributes_collection.to_h
450
+ end
435
451
 
436
452
  unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
437
453
  raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
@@ -458,6 +474,9 @@ module ActiveRecord
458
474
  end
459
475
 
460
476
  attributes_collection.each do |attributes|
477
+ if attributes.respond_to?(:permitted?)
478
+ attributes = attributes.to_h
479
+ end
461
480
  attributes = attributes.with_indifferent_access
462
481
 
463
482
  if attributes['id'].blank?
@@ -516,14 +535,14 @@ module ActiveRecord
516
535
 
517
536
  # Determines if a hash contains a truthy _destroy key.
518
537
  def has_destroy_flag?(hash)
519
- Type::Boolean.new.type_cast_from_user(hash['_destroy'])
538
+ Type::Boolean.new.cast(hash['_destroy'])
520
539
  end
521
540
 
522
541
  # Determines if a new record should be rejected by checking
523
542
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
524
543
  # association and evaluates to +true+.
525
544
  def reject_new_record?(association_name, attributes)
526
- will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
545
+ has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
527
546
  end
528
547
 
529
548
  # Determines if a record with the particular +attributes+ should be
@@ -532,8 +551,7 @@ module ActiveRecord
532
551
  #
533
552
  # Returns false if there is a +destroy_flag+ on the attributes.
534
553
  def call_reject_if(association_name, attributes)
535
- return false if will_be_destroyed?(association_name, attributes)
536
-
554
+ return false if has_destroy_flag?(attributes)
537
555
  case callback = self.nested_attributes_options[association_name][:reject_if]
538
556
  when Symbol
539
557
  method(callback).arity == 0 ? send(callback) : send(callback, attributes)
@@ -542,17 +560,10 @@ module ActiveRecord
542
560
  end
543
561
  end
544
562
 
545
- # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
546
- def will_be_destroyed?(association_name, attributes)
547
- allow_destroy?(association_name) && has_destroy_flag?(attributes)
548
- end
549
-
550
- def allow_destroy?(association_name)
551
- self.nested_attributes_options[association_name][:allow_destroy]
552
- end
553
-
554
563
  def raise_nested_attributes_record_not_found!(association_name, record_id)
555
- raise RecordNotFound, "Couldn't find #{self.class._reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
564
+ model = self.class._reflect_on_association(association_name).klass.name
565
+ raise RecordNotFound.new("Couldn't find #{model} with ID=#{record_id} for #{self.class.name} with ID=#{id}",
566
+ model, 'id', record_id)
556
567
  end
557
568
  end
558
569
  end