activerecord 4.2.11.3 → 5.0.7.2

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 (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1638 -1132
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record.rb +7 -2
  8. data/lib/active_record/aggregations.rb +34 -21
  9. data/lib/active_record/association_relation.rb +7 -4
  10. data/lib/active_record/associations.rb +347 -218
  11. data/lib/active_record/associations/alias_tracker.rb +19 -16
  12. data/lib/active_record/associations/association.rb +22 -10
  13. data/lib/active_record/associations/association_scope.rb +75 -104
  14. data/lib/active_record/associations/belongs_to_association.rb +21 -32
  15. data/lib/active_record/associations/builder/association.rb +28 -34
  16. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  17. data/lib/active_record/associations/builder/collection_association.rb +7 -19
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +16 -11
  19. data/lib/active_record/associations/builder/has_many.rb +4 -4
  20. data/lib/active_record/associations/builder/has_one.rb +11 -6
  21. data/lib/active_record/associations/builder/singular_association.rb +13 -11
  22. data/lib/active_record/associations/collection_association.rb +85 -69
  23. data/lib/active_record/associations/collection_proxy.rb +104 -46
  24. data/lib/active_record/associations/foreign_association.rb +1 -1
  25. data/lib/active_record/associations/has_many_association.rb +21 -78
  26. data/lib/active_record/associations/has_many_through_association.rb +6 -47
  27. data/lib/active_record/associations/has_one_association.rb +12 -5
  28. data/lib/active_record/associations/join_dependency.rb +38 -22
  29. data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +14 -4
  32. data/lib/active_record/associations/preloader/association.rb +52 -71
  33. data/lib/active_record/associations/preloader/collection_association.rb +0 -7
  34. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +0 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +36 -17
  38. data/lib/active_record/associations/singular_association.rb +13 -1
  39. data/lib/active_record/associations/through_association.rb +12 -4
  40. data/lib/active_record/attribute.rb +69 -19
  41. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  42. data/lib/active_record/attribute_assignment.rb +19 -140
  43. data/lib/active_record/attribute_decorators.rb +6 -5
  44. data/lib/active_record/attribute_methods.rb +69 -44
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
  46. data/lib/active_record/attribute_methods/dirty.rb +46 -86
  47. data/lib/active_record/attribute_methods/primary_key.rb +16 -3
  48. data/lib/active_record/attribute_methods/query.rb +2 -2
  49. data/lib/active_record/attribute_methods/read.rb +31 -59
  50. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
  52. data/lib/active_record/attribute_methods/write.rb +13 -37
  53. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  54. data/lib/active_record/attribute_set.rb +32 -3
  55. data/lib/active_record/attribute_set/builder.rb +42 -16
  56. data/lib/active_record/attributes.rb +199 -81
  57. data/lib/active_record/autosave_association.rb +54 -17
  58. data/lib/active_record/base.rb +32 -23
  59. data/lib/active_record/callbacks.rb +39 -43
  60. data/lib/active_record/coders/json.rb +1 -1
  61. data/lib/active_record/coders/yaml_column.rb +20 -8
  62. data/lib/active_record/collection_cache_key.rb +50 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
  76. data/lib/active_record/connection_adapters/column.rb +28 -43
  77. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  78. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  79. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  80. data/lib/active_record/connection_adapters/mysql/database_statements.rb +108 -0
  81. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  82. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  83. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  87. data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -166
  88. data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
  89. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
  90. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
  93. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
  95. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -3
  98. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  99. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  102. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +56 -19
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
  116. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  121. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +151 -194
  122. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  123. data/lib/active_record/connection_handling.rb +37 -14
  124. data/lib/active_record/core.rb +92 -108
  125. data/lib/active_record/counter_cache.rb +13 -24
  126. data/lib/active_record/dynamic_matchers.rb +1 -20
  127. data/lib/active_record/enum.rb +116 -76
  128. data/lib/active_record/errors.rb +87 -48
  129. data/lib/active_record/explain.rb +20 -9
  130. data/lib/active_record/explain_registry.rb +1 -1
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/file.rb +26 -5
  133. data/lib/active_record/fixtures.rb +77 -41
  134. data/lib/active_record/gem_version.rb +4 -4
  135. data/lib/active_record/inheritance.rb +32 -40
  136. data/lib/active_record/integration.rb +17 -14
  137. data/lib/active_record/internal_metadata.rb +56 -0
  138. data/lib/active_record/legacy_yaml_adapter.rb +18 -2
  139. data/lib/active_record/locale/en.yml +3 -2
  140. data/lib/active_record/locking/optimistic.rb +15 -15
  141. data/lib/active_record/locking/pessimistic.rb +1 -1
  142. data/lib/active_record/log_subscriber.rb +48 -24
  143. data/lib/active_record/migration.rb +362 -111
  144. data/lib/active_record/migration/command_recorder.rb +59 -18
  145. data/lib/active_record/migration/compatibility.rb +126 -0
  146. data/lib/active_record/model_schema.rb +270 -73
  147. data/lib/active_record/nested_attributes.rb +58 -29
  148. data/lib/active_record/no_touching.rb +4 -0
  149. data/lib/active_record/null_relation.rb +16 -8
  150. data/lib/active_record/persistence.rb +152 -90
  151. data/lib/active_record/query_cache.rb +18 -23
  152. data/lib/active_record/querying.rb +12 -11
  153. data/lib/active_record/railtie.rb +23 -16
  154. data/lib/active_record/railties/controller_runtime.rb +1 -1
  155. data/lib/active_record/railties/databases.rake +52 -41
  156. data/lib/active_record/readonly_attributes.rb +1 -1
  157. data/lib/active_record/reflection.rb +302 -115
  158. data/lib/active_record/relation.rb +187 -120
  159. data/lib/active_record/relation/batches.rb +141 -36
  160. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  161. data/lib/active_record/relation/calculations.rb +92 -117
  162. data/lib/active_record/relation/delegation.rb +8 -20
  163. data/lib/active_record/relation/finder_methods.rb +173 -89
  164. data/lib/active_record/relation/from_clause.rb +32 -0
  165. data/lib/active_record/relation/merger.rb +16 -42
  166. data/lib/active_record/relation/predicate_builder.rb +120 -107
  167. data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
  168. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  169. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  170. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  171. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  172. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  173. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  174. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  175. data/lib/active_record/relation/query_attribute.rb +19 -0
  176. data/lib/active_record/relation/query_methods.rb +308 -244
  177. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  178. data/lib/active_record/relation/spawn_methods.rb +4 -7
  179. data/lib/active_record/relation/where_clause.rb +174 -0
  180. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  181. data/lib/active_record/result.rb +11 -4
  182. data/lib/active_record/runtime_registry.rb +1 -1
  183. data/lib/active_record/sanitization.rb +105 -66
  184. data/lib/active_record/schema.rb +26 -22
  185. data/lib/active_record/schema_dumper.rb +54 -37
  186. data/lib/active_record/schema_migration.rb +11 -14
  187. data/lib/active_record/scoping.rb +34 -16
  188. data/lib/active_record/scoping/default.rb +28 -10
  189. data/lib/active_record/scoping/named.rb +59 -26
  190. data/lib/active_record/secure_token.rb +38 -0
  191. data/lib/active_record/serialization.rb +3 -5
  192. data/lib/active_record/statement_cache.rb +17 -15
  193. data/lib/active_record/store.rb +8 -3
  194. data/lib/active_record/suppressor.rb +58 -0
  195. data/lib/active_record/table_metadata.rb +69 -0
  196. data/lib/active_record/tasks/database_tasks.rb +66 -49
  197. data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
  198. data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
  199. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  200. data/lib/active_record/timestamp.rb +20 -9
  201. data/lib/active_record/touch_later.rb +63 -0
  202. data/lib/active_record/transactions.rb +139 -57
  203. data/lib/active_record/type.rb +66 -17
  204. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  205. data/lib/active_record/type/date.rb +2 -45
  206. data/lib/active_record/type/date_time.rb +2 -49
  207. data/lib/active_record/type/internal/abstract_json.rb +33 -0
  208. data/lib/active_record/type/internal/timezone.rb +15 -0
  209. data/lib/active_record/type/serialized.rb +15 -14
  210. data/lib/active_record/type/time.rb +10 -16
  211. data/lib/active_record/type/type_map.rb +4 -4
  212. data/lib/active_record/type_caster.rb +7 -0
  213. data/lib/active_record/type_caster/connection.rb +29 -0
  214. data/lib/active_record/type_caster/map.rb +19 -0
  215. data/lib/active_record/validations.rb +33 -32
  216. data/lib/active_record/validations/absence.rb +23 -0
  217. data/lib/active_record/validations/associated.rb +10 -3
  218. data/lib/active_record/validations/length.rb +24 -0
  219. data/lib/active_record/validations/presence.rb +11 -12
  220. data/lib/active_record/validations/uniqueness.rb +33 -33
  221. data/lib/rails/generators/active_record/migration.rb +15 -0
  222. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
  223. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
  224. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
  225. data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
  226. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  227. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  228. metadata +58 -34
  229. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  230. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  231. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  232. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  233. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  234. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  235. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  236. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  237. data/lib/active_record/type/big_integer.rb +0 -13
  238. data/lib/active_record/type/binary.rb +0 -50
  239. data/lib/active_record/type/boolean.rb +0 -31
  240. data/lib/active_record/type/decimal.rb +0 -64
  241. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  242. data/lib/active_record/type/decorator.rb +0 -14
  243. data/lib/active_record/type/float.rb +0 -19
  244. data/lib/active_record/type/integer.rb +0 -59
  245. data/lib/active_record/type/mutable.rb +0 -16
  246. data/lib/active_record/type/numeric.rb +0 -36
  247. data/lib/active_record/type/string.rb +0 -40
  248. data/lib/active_record/type/text.rb +0 -11
  249. data/lib/active_record/type/time_value.rb +0 -38
  250. data/lib/active_record/type/unsigned_integer.rb +0 -15
  251. data/lib/active_record/type/value.rb +0 -110
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  class DatabaseAlreadyExists < StandardError; end # :nodoc:
6
6
  class DatabaseNotSupported < StandardError; end # :nodoc:
7
7
 
8
- # <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
8
+ # ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates
9
9
  # logic behind common tasks used to manage database and migrations.
10
10
  #
11
11
  # The tasks defined here are used with Rake tasks provided by Active Record.
@@ -18,15 +18,15 @@ module ActiveRecord
18
18
  #
19
19
  # The possible config values are:
20
20
  #
21
- # * +env+: current environment (like Rails.env).
22
- # * +database_configuration+: configuration of your databases (as in +config/database.yml+).
23
- # * +db_dir+: your +db+ directory.
24
- # * +fixtures_path+: a path to fixtures directory.
25
- # * +migrations_paths+: a list of paths to directories with migrations.
26
- # * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
27
- # * +root+: a path to the root of the application.
21
+ # * +env+: current environment (like Rails.env).
22
+ # * +database_configuration+: configuration of your databases (as in +config/database.yml+).
23
+ # * +db_dir+: your +db+ directory.
24
+ # * +fixtures_path+: a path to fixtures directory.
25
+ # * +migrations_paths+: a list of paths to directories with migrations.
26
+ # * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
27
+ # * +root+: a path to the root of the application.
28
28
  #
29
- # Example usage of +DatabaseTasks+ outside Rails could look as such:
29
+ # Example usage of DatabaseTasks outside Rails could look as such:
30
30
  #
31
31
  # include ActiveRecord::Tasks
32
32
  # DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
@@ -42,6 +42,22 @@ module ActiveRecord
42
42
 
43
43
  LOCAL_HOSTS = ['127.0.0.1', 'localhost']
44
44
 
45
+ def check_protected_environments!
46
+ unless ENV['DISABLE_DATABASE_ENVIRONMENT_CHECK']
47
+ current = ActiveRecord::Migrator.current_environment
48
+ stored = ActiveRecord::Migrator.last_stored_environment
49
+
50
+ if ActiveRecord::Migrator.protected_environment?
51
+ raise ActiveRecord::ProtectedEnvironmentError.new(stored)
52
+ end
53
+
54
+ if stored && stored != current
55
+ raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
56
+ end
57
+ end
58
+ rescue ActiveRecord::NoDatabaseError
59
+ end
60
+
45
61
  def register_task(pattern, task)
46
62
  @tasks ||= {}
47
63
  @tasks[pattern] = task
@@ -91,15 +107,21 @@ module ActiveRecord
91
107
  def create(*arguments)
92
108
  configuration = arguments.first
93
109
  class_for_adapter(configuration['adapter']).new(*arguments).create
110
+ $stdout.puts "Created database '#{configuration['database']}'"
94
111
  rescue DatabaseAlreadyExists
95
- $stderr.puts "#{configuration['database']} already exists"
112
+ $stderr.puts "Database '#{configuration['database']}' already exists"
96
113
  rescue Exception => error
97
- $stderr.puts error, *(error.backtrace)
114
+ $stderr.puts error
98
115
  $stderr.puts "Couldn't create database for #{configuration.inspect}"
116
+ raise
99
117
  end
100
118
 
101
119
  def create_all
120
+ old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
102
121
  each_local_configuration { |configuration| create configuration }
122
+ if old_pool
123
+ ActiveRecord::Base.connection_handler.establish_connection(old_pool.spec)
124
+ end
103
125
  end
104
126
 
105
127
  def create_current(environment = env)
@@ -112,11 +134,13 @@ module ActiveRecord
112
134
  def drop(*arguments)
113
135
  configuration = arguments.first
114
136
  class_for_adapter(configuration['adapter']).new(*arguments).drop
137
+ $stdout.puts "Dropped database '#{configuration['database']}'"
115
138
  rescue ActiveRecord::NoDatabaseError
116
139
  $stderr.puts "Database '#{configuration['database']}' does not exist"
117
140
  rescue Exception => error
118
- $stderr.puts error, *(error.backtrace)
119
- $stderr.puts "Couldn't drop #{configuration['database']}"
141
+ $stderr.puts error
142
+ $stderr.puts "Couldn't drop database '#{configuration['database']}'"
143
+ raise
120
144
  end
121
145
 
122
146
  def drop_all
@@ -191,59 +215,52 @@ module ActiveRecord
191
215
  class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
192
216
  end
193
217
 
194
- def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
195
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
196
- This method will act on a specific connection in the future.
197
- To act on the current connection, use `load_schema_current` instead.
198
- MSG
199
-
200
- load_schema_current(format, file)
201
- end
202
-
203
- def schema_file(format = ActiveRecord::Base.schema_format)
204
- case format
205
- when :ruby
206
- File.join(db_dir, "schema.rb")
207
- when :sql
208
- File.join(db_dir, "structure.sql")
209
- end
210
- end
211
-
212
- # This method is the successor of +load_schema+. We should rename it
213
- # after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
214
- def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
218
+ def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env) # :nodoc:
215
219
  file ||= schema_file(format)
216
220
 
221
+ check_schema_file(file)
222
+ ActiveRecord::Base.establish_connection(configuration)
223
+
217
224
  case format
218
225
  when :ruby
219
- check_schema_file(file)
220
- ActiveRecord::Base.establish_connection(configuration)
221
226
  load(file)
222
227
  when :sql
223
- check_schema_file(file)
224
228
  structure_load(configuration, file)
225
229
  else
226
230
  raise ArgumentError, "unknown format #{format.inspect}"
227
231
  end
232
+ ActiveRecord::InternalMetadata.create_table
233
+ ActiveRecord::InternalMetadata[:environment] = environment
234
+ end
235
+
236
+ def load_schema_for(*args)
237
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
238
+ This method was renamed to `#load_schema` and will be removed in the future.
239
+ Use `#load_schema` instead.
240
+ MSG
241
+ load_schema(*args)
228
242
  end
229
243
 
230
- def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
231
- if File.exist?(file || schema_file(format))
232
- load_schema_current(format, file, environment)
244
+ def schema_file(format = ActiveRecord::Base.schema_format)
245
+ case format
246
+ when :ruby
247
+ File.join(db_dir, "schema.rb")
248
+ when :sql
249
+ File.join(db_dir, "structure.sql")
233
250
  end
234
251
  end
235
252
 
236
253
  def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
237
- each_current_configuration(environment) { |configuration|
238
- load_schema_for configuration, format, file
254
+ each_current_configuration(environment) { |configuration, configuration_environment|
255
+ load_schema configuration, format, file, configuration_environment
239
256
  }
240
257
  ActiveRecord::Base.establish_connection(environment.to_sym)
241
258
  end
242
259
 
243
260
  def check_schema_file(filename)
244
261
  unless File.exist?(filename)
245
- message = %{#{filename} doesn't exist yet. Run `rake db:migrate` to create it, then try again.}
246
- message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails)
262
+ message = %{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}
263
+ message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails.root)
247
264
  Kernel.abort message
248
265
  end
249
266
  end
@@ -270,12 +287,12 @@ module ActiveRecord
270
287
 
271
288
  def each_current_configuration(environment)
272
289
  environments = [environment]
273
- # add test environment only if no RAILS_ENV was specified.
274
- environments << 'test' if environment == 'development' && ENV['RAILS_ENV'].nil?
290
+ environments << 'test' if environment == 'development'
291
+
292
+ ActiveRecord::Base.configurations.slice(*environments).each do |configuration_environment, configuration|
293
+ next unless configuration["database"]
275
294
 
276
- configurations = ActiveRecord::Base.configurations.values_at(*environments)
277
- configurations.compact.each do |configuration|
278
- yield configuration unless configuration['database'].blank?
295
+ yield configuration, configuration_environment
279
296
  end
280
297
  end
281
298
 
@@ -1,8 +1,6 @@
1
1
  module ActiveRecord
2
2
  module Tasks # :nodoc:
3
3
  class MySQLDatabaseTasks # :nodoc:
4
- DEFAULT_CHARSET = ENV['CHARSET'] || 'utf8'
5
- DEFAULT_COLLATION = ENV['COLLATION'] || 'utf8_unicode_ci'
6
4
  ACCESS_DENIED_ERROR = 1045
7
5
 
8
6
  delegate :connection, :establish_connection, to: ActiveRecord::Base
@@ -23,7 +21,7 @@ module ActiveRecord
23
21
  end
24
22
  rescue error_class => error
25
23
  if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR
26
- $stdout.print error.error
24
+ $stdout.print error.message
27
25
  establish_connection root_configuration_without_database
28
26
  connection.create_database configuration['database'], creation_options
29
27
  if configuration['username'] != 'root'
@@ -59,6 +57,7 @@ module ActiveRecord
59
57
  args = prepare_command_options
60
58
  args.concat(["--result-file", "#{filename}"])
61
59
  args.concat(["--no-data"])
60
+ args.concat(["--routines"])
62
61
  args.concat(["#{configuration['database']}"])
63
62
 
64
63
  run_cmd('mysqldump', args, 'dumping')
@@ -86,12 +85,6 @@ module ActiveRecord
86
85
  Hash.new.tap do |options|
87
86
  options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
88
87
  options[:collation] = configuration['collation'] if configuration.include? 'collation'
89
-
90
- # Set default charset only when collation isn't set.
91
- options[:charset] ||= DEFAULT_CHARSET unless options[:collation]
92
-
93
- # Set default collation only when charset is also default.
94
- options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET
95
88
  end
96
89
  end
97
90
 
@@ -101,8 +94,6 @@ module ActiveRecord
101
94
  ArJdbcMySQL::Error
102
95
  elsif defined?(Mysql2)
103
96
  Mysql2::Error
104
- elsif defined?(Mysql)
105
- Mysql::Error
106
97
  else
107
98
  StandardError
108
99
  end
@@ -129,7 +120,7 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
129
120
  end
130
121
 
131
122
  def prepare_command_options
132
- {
123
+ args = {
133
124
  'host' => '--host',
134
125
  'port' => '--port',
135
126
  'socket' => '--socket',
@@ -142,6 +133,8 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
142
133
  'sslcipher' => '--ssl-cipher',
143
134
  'sslkey' => '--ssl-key'
144
135
  }.map { |opt, arg| "#{arg}=#{configuration[opt]}" if configuration[opt] }.compact
136
+
137
+ args
145
138
  end
146
139
 
147
140
  def run_cmd(cmd, args, action)
@@ -149,8 +142,7 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
149
142
  end
150
143
 
151
144
  def run_cmd_error(cmd, args, action)
152
- msg = "failed to execute:\n"
153
- msg << "#{cmd}"
145
+ msg = "failed to execute: `#{cmd}`\n"
154
146
  msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
155
147
  msg
156
148
  end
@@ -16,7 +16,7 @@ module ActiveRecord
16
16
  configuration.merge('encoding' => encoding)
17
17
  establish_connection configuration
18
18
  rescue ActiveRecord::StatementInvalid => error
19
- if /database .* already exists/ === error.message
19
+ if error.cause.is_a?(PG::DuplicateDatabase)
20
20
  raise DatabaseAlreadyExists
21
21
  else
22
22
  raise
@@ -44,8 +44,17 @@ module ActiveRecord
44
44
 
45
45
  def structure_dump(filename)
46
46
  set_psql_env
47
+
48
+ search_path = case ActiveRecord::Base.dump_schemas
49
+ when :schema_search_path
50
+ configuration['schema_search_path']
51
+ when :all
52
+ nil
53
+ when String
54
+ ActiveRecord::Base.dump_schemas
55
+ end
56
+
47
57
  args = ['-s', '-x', '-O', '-f', filename]
48
- search_path = configuration['schema_search_path']
49
58
  unless search_path.blank?
50
59
  args += search_path.split(',').map do |part|
51
60
  "--schema=#{part.strip}"
@@ -59,7 +68,7 @@ module ActiveRecord
59
68
  def structure_load(filename)
60
69
  set_psql_env
61
70
  args = [ '-q', '-f', filename, configuration['database'] ]
62
- run_cmd('psql', args, 'loading')
71
+ run_cmd('psql', args, 'loading' )
63
72
  end
64
73
 
65
74
  private
@@ -19,11 +19,15 @@ module ActiveRecord
19
19
  path = Pathname.new configuration['database']
20
20
  file = path.absolute? ? path.to_s : File.join(root, path)
21
21
 
22
- FileUtils.rm(file) if File.exist?(file)
22
+ FileUtils.rm(file)
23
+ rescue Errno::ENOENT => error
24
+ raise NoDatabaseError.new(error.message, error)
23
25
  end
24
26
 
25
27
  def purge
26
28
  drop
29
+ rescue NoDatabaseError
30
+ ensure
27
31
  create
28
32
  end
29
33
 
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
- # = Active Record Timestamp
2
+ # = Active Record \Timestamp
3
3
  #
4
4
  # Active Record automatically timestamps create and update operations if the
5
5
  # table has fields named <tt>created_at/created_on</tt> or
@@ -15,14 +15,25 @@ module ActiveRecord
15
15
  #
16
16
  # == Time Zone aware attributes
17
17
  #
18
- # By default, ActiveRecord::Base keeps all the datetime columns time zone aware by executing following code.
18
+ # Active Record keeps all the <tt>datetime</tt> and <tt>time</tt> columns
19
+ # timezone aware. By default, these values are stored in the database as UTC
20
+ # and converted back to the current <tt>Time.zone</tt> when pulled from the database.
19
21
  #
20
- # config.active_record.time_zone_aware_attributes = true
22
+ # This feature can be turned off completely by setting:
21
23
  #
22
- # This feature can easily be turned off by assigning value <tt>false</tt> .
24
+ # config.active_record.time_zone_aware_attributes = false
23
25
  #
24
- # If your attributes are time zone aware and you desire to skip time zone conversion to the current Time.zone
25
- # when reading certain attributes then you can do following:
26
+ # You can also specify that only <tt>datetime</tt> columns should be time-zone
27
+ # aware (while <tt>time</tt> should not) by setting:
28
+ #
29
+ # ActiveRecord::Base.time_zone_aware_types = [:datetime]
30
+ #
31
+ # You can also add database specific timezone aware types. For example, for PostgreSQL:
32
+ #
33
+ # ActiveRecord::Base.time_zone_aware_types += [:tsrange, :tstzrange]
34
+ #
35
+ # Finally, you can indicate specific attributes of a model for which time zone
36
+ # conversion should not applied, for instance by setting:
26
37
  #
27
38
  # class Topic < ActiveRecord::Base
28
39
  # self.skip_time_zone_conversion_for_attributes = [:written_on]
@@ -57,8 +68,8 @@ module ActiveRecord
57
68
  super
58
69
  end
59
70
 
60
- def _update_record(*args)
61
- if should_record_timestamps?
71
+ def _update_record(*args, touch: true, **options)
72
+ if touch && should_record_timestamps?
62
73
  current_time = current_time_from_proper_timezone
63
74
 
64
75
  timestamp_attributes_for_update_in_model.each do |column|
@@ -67,7 +78,7 @@ module ActiveRecord
67
78
  write_attribute(column, current_time)
68
79
  end
69
80
  end
70
- super
81
+ super(*args)
71
82
  end
72
83
 
73
84
  def should_record_timestamps?
@@ -0,0 +1,63 @@
1
+ module ActiveRecord
2
+ # = Active Record Touch Later
3
+ module TouchLater
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_commit_without_transaction_enrollment :touch_deferred_attributes
8
+ end
9
+
10
+ def touch_later(*names) # :nodoc:
11
+ unless persisted?
12
+ raise ActiveRecordError, <<-MSG.squish
13
+ cannot touch on a new or destroyed record object. Consider using
14
+ persisted?, new_record?, or destroyed? before touching
15
+ MSG
16
+ end
17
+
18
+ @_defer_touch_attrs ||= timestamp_attributes_for_update_in_model
19
+ @_defer_touch_attrs |= names
20
+ @_touch_time = current_time_from_proper_timezone
21
+
22
+ surreptitiously_touch @_defer_touch_attrs
23
+ self.class.connection.add_transaction_record self
24
+
25
+ # touch the parents as we are not calling the after_save callbacks
26
+ self.class.reflect_on_all_associations(:belongs_to).each do |r|
27
+ if touch = r.options[:touch]
28
+ ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, r.foreign_key, r.name, touch, :touch_later)
29
+ end
30
+ end
31
+ end
32
+
33
+ def touch(*names, time: nil) # :nodoc:
34
+ if has_defer_touch_attrs?
35
+ names |= @_defer_touch_attrs
36
+ end
37
+ super(*names, time: time)
38
+ end
39
+
40
+ private
41
+
42
+ def surreptitiously_touch(attrs)
43
+ attrs.each { |attr| write_attribute attr, @_touch_time }
44
+ clear_attribute_changes attrs
45
+ end
46
+
47
+ def touch_deferred_attributes
48
+ if has_defer_touch_attrs? && persisted?
49
+ touch(*@_defer_touch_attrs, time: @_touch_time)
50
+ @_defer_touch_attrs, @_touch_time = nil, nil
51
+ end
52
+ end
53
+
54
+ def has_defer_touch_attrs?
55
+ defined?(@_defer_touch_attrs) && @_defer_touch_attrs.present?
56
+ end
57
+
58
+ def belongs_to_touch_method
59
+ :touch_later
60
+ end
61
+
62
+ end
63
+ end
@@ -4,32 +4,23 @@ module ActiveRecord
4
4
  extend ActiveSupport::Concern
5
5
  #:nodoc:
6
6
  ACTIONS = [:create, :destroy, :update]
7
- #:nodoc:
8
- CALLBACK_WARN_MESSAGE = "Currently, Active Record suppresses errors raised " \
9
- "within `after_rollback`/`after_commit` callbacks and only print them to " \
10
- "the logs. In the next version, these errors will no longer be suppressed. " \
11
- "Instead, the errors will propagate normally just like in other Active " \
12
- "Record callbacks.\n" \
13
- "\n" \
14
- "You can opt into the new behavior and remove this warning by setting:\n" \
15
- "\n" \
16
- " config.active_record.raise_in_transactional_callbacks = true\n\n"
17
7
 
18
8
  included do
19
9
  define_callbacks :commit, :rollback,
20
- terminator: ->(_, result) { result == false },
10
+ :before_commit,
11
+ :before_commit_without_transaction_enrollment,
12
+ :commit_without_transaction_enrollment,
13
+ :rollback_without_transaction_enrollment,
14
+ terminator: deprecated_false_terminator,
21
15
  scope: [:kind, :name]
22
-
23
- mattr_accessor :raise_in_transactional_callbacks, instance_writer: false
24
- self.raise_in_transactional_callbacks = false
25
16
  end
26
17
 
27
18
  # = Active Record Transactions
28
19
  #
29
- # Transactions are protective blocks where SQL statements are only permanent
20
+ # \Transactions are protective blocks where SQL statements are only permanent
30
21
  # if they can all succeed as one atomic action. The classic example is a
31
22
  # transfer between two accounts where you can only have a deposit if the
32
- # withdrawal succeeded and vice versa. Transactions enforce the integrity of
23
+ # withdrawal succeeded and vice versa. \Transactions enforce the integrity of
33
24
  # the database and guard the data against program errors or database
34
25
  # break-downs. So basically you should use transaction blocks whenever you
35
26
  # have a number of statements that must be executed together or not at all.
@@ -49,20 +40,20 @@ module ActiveRecord
49
40
  #
50
41
  # == Different Active Record classes in a single transaction
51
42
  #
52
- # Though the transaction class method is called on some Active Record class,
43
+ # Though the #transaction class method is called on some Active Record class,
53
44
  # the objects within the transaction block need not all be instances of
54
45
  # that class. This is because transactions are per-database connection, not
55
46
  # per-model.
56
47
  #
57
48
  # In this example a +balance+ record is transactionally saved even
58
- # though +transaction+ is called on the +Account+ class:
49
+ # though #transaction is called on the +Account+ class:
59
50
  #
60
51
  # Account.transaction do
61
52
  # balance.save!
62
53
  # account.save!
63
54
  # end
64
55
  #
65
- # The +transaction+ method is also available as a model instance method.
56
+ # The #transaction method is also available as a model instance method.
66
57
  # For example, you can also do this:
67
58
  #
68
59
  # balance.transaction do
@@ -89,7 +80,8 @@ module ActiveRecord
89
80
  #
90
81
  # == +save+ and +destroy+ are automatically wrapped in a transaction
91
82
  #
92
- # Both +save+ and +destroy+ come wrapped in a transaction that ensures
83
+ # Both {#save}[rdoc-ref:Persistence#save] and
84
+ # {#destroy}[rdoc-ref:Persistence#destroy] come wrapped in a transaction that ensures
93
85
  # that whatever you do in validations or callbacks will happen under its
94
86
  # protected cover. So you can use validations to check for values that
95
87
  # the transaction depends on or you can raise exceptions in the callbacks
@@ -98,7 +90,7 @@ module ActiveRecord
98
90
  # As a consequence changes to the database are not seen outside your connection
99
91
  # until the operation is complete. For example, if you try to update the index
100
92
  # of a search engine in +after_save+ the indexer won't see the updated record.
101
- # The +after_commit+ callback is the only one that is triggered once the update
93
+ # The #after_commit callback is the only one that is triggered once the update
102
94
  # is committed. See below.
103
95
  #
104
96
  # == Exception handling and rolling back
@@ -107,11 +99,11 @@ module ActiveRecord
107
99
  # be propagated (after triggering the ROLLBACK), so you should be ready to
108
100
  # catch those in your application code.
109
101
  #
110
- # One exception is the <tt>ActiveRecord::Rollback</tt> exception, which will trigger
102
+ # One exception is the ActiveRecord::Rollback exception, which will trigger
111
103
  # a ROLLBACK when raised, but not be re-raised by the transaction block.
112
104
  #
113
- # *Warning*: one should not catch <tt>ActiveRecord::StatementInvalid</tt> exceptions
114
- # inside a transaction block. <tt>ActiveRecord::StatementInvalid</tt> exceptions indicate that an
105
+ # *Warning*: one should not catch ActiveRecord::StatementInvalid exceptions
106
+ # inside a transaction block. ActiveRecord::StatementInvalid exceptions indicate that an
115
107
  # error occurred at the database level, for example when a unique constraint
116
108
  # is violated. On some database systems, such as PostgreSQL, database errors
117
109
  # inside a transaction cause the entire transaction to become unusable
@@ -132,16 +124,16 @@ module ActiveRecord
132
124
  # # statement will cause a PostgreSQL error, even though the unique
133
125
  # # constraint is no longer violated:
134
126
  # Number.create(i: 1)
135
- # # => "PGError: ERROR: current transaction is aborted, commands
127
+ # # => "PG::Error: ERROR: current transaction is aborted, commands
136
128
  # # ignored until end of transaction block"
137
129
  # end
138
130
  #
139
131
  # One should restart the entire transaction if an
140
- # <tt>ActiveRecord::StatementInvalid</tt> occurred.
132
+ # ActiveRecord::StatementInvalid occurred.
141
133
  #
142
134
  # == Nested transactions
143
135
  #
144
- # +transaction+ calls can be nested. By default, this makes all database
136
+ # #transaction calls can be nested. By default, this makes all database
145
137
  # statements in the nested transaction block become part of the parent
146
138
  # transaction. For example, the following behavior may be surprising:
147
139
  #
@@ -153,7 +145,7 @@ module ActiveRecord
153
145
  # end
154
146
  # end
155
147
  #
156
- # creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt>
148
+ # creates both "Kotori" and "Nemu". Reason is the ActiveRecord::Rollback
157
149
  # exception in the nested block does not issue a ROLLBACK. Since these exceptions
158
150
  # are captured in transaction blocks, the parent block does not see it and the
159
151
  # real transaction is committed.
@@ -177,22 +169,22 @@ module ActiveRecord
177
169
  # writing, the only database that we're aware of that supports true nested
178
170
  # transactions, is MS-SQL. Because of this, Active Record emulates nested
179
171
  # transactions by using savepoints on MySQL and PostgreSQL. See
180
- # http://dev.mysql.com/doc/refman/5.6/en/savepoint.html
172
+ # http://dev.mysql.com/doc/refman/5.7/en/savepoint.html
181
173
  # for more information about savepoints.
182
174
  #
183
- # === Callbacks
175
+ # === \Callbacks
184
176
  #
185
177
  # There are two types of callbacks associated with committing and rolling back transactions:
186
- # +after_commit+ and +after_rollback+.
178
+ # #after_commit and #after_rollback.
187
179
  #
188
- # +after_commit+ callbacks are called on every record saved or destroyed within a
189
- # transaction immediately after the transaction is committed. +after_rollback+ callbacks
180
+ # #after_commit callbacks are called on every record saved or destroyed within a
181
+ # transaction immediately after the transaction is committed. #after_rollback callbacks
190
182
  # are called on every record saved or destroyed within a transaction immediately after the
191
183
  # transaction or savepoint is rolled back.
192
184
  #
193
185
  # These callbacks are useful for interacting with other systems since you will be guaranteed
194
186
  # that the callback is only executed when the database is in a permanent state. For example,
195
- # +after_commit+ is a good spot to put in a hook to clearing a cache since clearing it from
187
+ # #after_commit is a good spot to put in a hook to clearing a cache since clearing it from
196
188
  # within a transaction could trigger the cache to be regenerated before the database is updated.
197
189
  #
198
190
  # === Caveats
@@ -206,20 +198,24 @@ module ActiveRecord
206
198
  # automatically released. The following example demonstrates the problem:
207
199
  #
208
200
  # Model.connection.transaction do # BEGIN
209
- # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
201
+ # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
210
202
  # Model.connection.create_table(...) # active_record_1 now automatically released
211
- # end # RELEASE savepoint active_record_1
203
+ # end # RELEASE SAVEPOINT active_record_1
212
204
  # # ^^^^ BOOM! database error!
213
205
  # end
214
206
  #
215
207
  # Note that "TRUNCATE" is also a MySQL DDL statement!
216
208
  module ClassMethods
217
- # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
209
+ # See the ConnectionAdapters::DatabaseStatements#transaction API docs.
218
210
  def transaction(options = {}, &block)
219
- # See the ConnectionAdapters::DatabaseStatements#transaction API docs.
220
211
  connection.transaction(options, &block)
221
212
  end
222
213
 
214
+ def before_commit(*args, &block) # :nodoc:
215
+ set_options_for_callbacks!(args)
216
+ set_callback(:before_commit, :before, *args, &block)
217
+ end
218
+
223
219
  # This callback is called after a record has been created, updated, or destroyed.
224
220
  #
225
221
  # You can specify that the callback should only be fired by a certain action with
@@ -232,32 +228,69 @@ module ActiveRecord
232
228
  # after_commit :do_foo_bar, on: [:create, :update]
233
229
  # after_commit :do_bar_baz, on: [:update, :destroy]
234
230
  #
235
- # Note that transactional fixtures do not play well with this feature. Please
236
- # use the +test_after_commit+ gem to have these hooks fired in tests.
237
231
  def after_commit(*args, &block)
238
232
  set_options_for_callbacks!(args)
239
233
  set_callback(:commit, :after, *args, &block)
240
- unless ActiveRecord::Base.raise_in_transactional_callbacks
241
- ActiveSupport::Deprecation.warn(CALLBACK_WARN_MESSAGE)
242
- end
234
+ end
235
+
236
+ # Shortcut for <tt>after_commit :hook, on: :create</tt>.
237
+ def after_create_commit(*args, &block)
238
+ set_options_for_callbacks!(args, on: :create)
239
+ set_callback(:commit, :after, *args, &block)
240
+ end
241
+
242
+ # Shortcut for <tt>after_commit :hook, on: :update</tt>.
243
+ def after_update_commit(*args, &block)
244
+ set_options_for_callbacks!(args, on: :update)
245
+ set_callback(:commit, :after, *args, &block)
246
+ end
247
+
248
+ # Shortcut for <tt>after_commit :hook, on: :destroy</tt>.
249
+ def after_destroy_commit(*args, &block)
250
+ set_options_for_callbacks!(args, on: :destroy)
251
+ set_callback(:commit, :after, *args, &block)
243
252
  end
244
253
 
245
254
  # This callback is called after a create, update, or destroy are rolled back.
246
255
  #
247
- # Please check the documentation of +after_commit+ for options.
256
+ # Please check the documentation of #after_commit for options.
248
257
  def after_rollback(*args, &block)
249
258
  set_options_for_callbacks!(args)
250
259
  set_callback(:rollback, :after, *args, &block)
251
- unless ActiveRecord::Base.raise_in_transactional_callbacks
252
- ActiveSupport::Deprecation.warn(CALLBACK_WARN_MESSAGE)
253
- end
260
+ end
261
+
262
+ def before_commit_without_transaction_enrollment(*args, &block) # :nodoc:
263
+ set_options_for_callbacks!(args)
264
+ set_callback(:before_commit_without_transaction_enrollment, :before, *args, &block)
265
+ end
266
+
267
+ def after_commit_without_transaction_enrollment(*args, &block) # :nodoc:
268
+ set_options_for_callbacks!(args)
269
+ set_callback(:commit_without_transaction_enrollment, :after, *args, &block)
270
+ end
271
+
272
+ def after_rollback_without_transaction_enrollment(*args, &block) # :nodoc:
273
+ set_options_for_callbacks!(args)
274
+ set_callback(:rollback_without_transaction_enrollment, :after, *args, &block)
275
+ end
276
+
277
+ def raise_in_transactional_callbacks
278
+ ActiveSupport::Deprecation.warn('ActiveRecord::Base.raise_in_transactional_callbacks is deprecated and will be removed without replacement.')
279
+ true
280
+ end
281
+
282
+ def raise_in_transactional_callbacks=(value)
283
+ ActiveSupport::Deprecation.warn('ActiveRecord::Base.raise_in_transactional_callbacks= is deprecated, has no effect and will be removed without replacement.')
284
+ value
254
285
  end
255
286
 
256
287
  private
257
288
 
258
- def set_options_for_callbacks!(args)
259
- options = args.last
260
- if options.is_a?(Hash) && options[:on]
289
+ def set_options_for_callbacks!(args, enforced_options = {})
290
+ options = args.extract_options!.merge!(enforced_options)
291
+ args << options
292
+
293
+ if options[:on]
261
294
  fire_on = Array(options[:on])
262
295
  assert_valid_transaction_action(fire_on)
263
296
  options[:if] = Array(options[:if])
@@ -306,26 +339,37 @@ module ActiveRecord
306
339
  clear_transaction_record_state
307
340
  end
308
341
 
309
- # Call the +after_commit+ callbacks.
342
+ def before_committed! # :nodoc:
343
+ _run_before_commit_without_transaction_enrollment_callbacks
344
+ _run_before_commit_callbacks
345
+ end
346
+
347
+ # Call the #after_commit callbacks.
310
348
  #
311
349
  # Ensure that it is not called if the object was never persisted (failed create),
312
350
  # but call it after the commit of a destroyed object.
313
- def committed!(should_run_callbacks = true) #:nodoc:
314
- _run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
351
+ def committed!(should_run_callbacks: true) #:nodoc:
352
+ if should_run_callbacks && destroyed? || persisted?
353
+ _run_commit_without_transaction_enrollment_callbacks
354
+ _run_commit_callbacks
355
+ end
315
356
  ensure
316
357
  force_clear_transaction_record_state
317
358
  end
318
359
 
319
- # Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record
360
+ # Call the #after_rollback callbacks. The +force_restore_state+ argument indicates if the record
320
361
  # state should be rolled back to the beginning or just to the last savepoint.
321
- def rolledback!(force_restore_state = false, should_run_callbacks = true) #:nodoc:
322
- _run_rollback_callbacks if should_run_callbacks
362
+ def rolledback!(force_restore_state: false, should_run_callbacks: true) #:nodoc:
363
+ if should_run_callbacks
364
+ _run_rollback_callbacks
365
+ _run_rollback_without_transaction_enrollment_callbacks
366
+ end
323
367
  ensure
324
368
  restore_transaction_record_state(force_restore_state)
325
369
  clear_transaction_record_state
326
370
  end
327
371
 
328
- # Add the record to the current transaction so that the +after_rollback+ and +after_commit+ callbacks
372
+ # Add the record to the current transaction so that the #after_rollback and #after_commit callbacks
329
373
  # can be called.
330
374
  def add_to_transaction
331
375
  if has_transactional_callbacks?
@@ -423,5 +467,43 @@ module ActiveRecord
423
467
  end
424
468
  end
425
469
  end
470
+
471
+ private
472
+
473
+ def set_transaction_state(state) # :nodoc:
474
+ @transaction_state = state
475
+ end
476
+
477
+ def has_transactional_callbacks? # :nodoc:
478
+ !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
479
+ end
480
+
481
+ # Updates the attributes on this particular Active Record object so that
482
+ # if it's associated with a transaction, then the state of the Active Record
483
+ # object will be updated to reflect the current state of the transaction
484
+ #
485
+ # The +@transaction_state+ variable stores the states of the associated
486
+ # transaction. This relies on the fact that a transaction can only be in
487
+ # one rollback or commit (otherwise a list of states would be required)
488
+ # Each Active Record object inside of a transaction carries that transaction's
489
+ # TransactionState.
490
+ #
491
+ # This method checks to see if the ActiveRecord object's state reflects
492
+ # the TransactionState, and rolls back or commits the Active Record object
493
+ # as appropriate.
494
+ #
495
+ # Since Active Record objects can be inside multiple transactions, this
496
+ # method recursively goes through the parent of the TransactionState and
497
+ # checks if the Active Record object reflects the state of the object.
498
+ def sync_with_transaction_state
499
+ update_attributes_from_transaction_state(@transaction_state)
500
+ end
501
+
502
+ def update_attributes_from_transaction_state(transaction_state)
503
+ if transaction_state && transaction_state.finalized?
504
+ restore_transaction_record_state if transaction_state.rolledback?
505
+ clear_transaction_record_state
506
+ end
507
+ end
426
508
  end
427
509
  end