activerecord 4.1.15 → 4.2.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1792
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +4 -0
  5. data/lib/active_record/aggregations.rb +15 -8
  6. data/lib/active_record/association_relation.rb +13 -0
  7. data/lib/active_record/associations.rb +158 -49
  8. data/lib/active_record/associations/alias_tracker.rb +3 -12
  9. data/lib/active_record/associations/association.rb +16 -4
  10. data/lib/active_record/associations/association_scope.rb +83 -38
  11. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  12. data/lib/active_record/associations/builder/association.rb +15 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  16. data/lib/active_record/associations/builder/has_many.rb +1 -1
  17. data/lib/active_record/associations/builder/has_one.rb +2 -2
  18. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  19. data/lib/active_record/associations/collection_association.rb +63 -27
  20. data/lib/active_record/associations/collection_proxy.rb +29 -35
  21. data/lib/active_record/associations/foreign_association.rb +11 -0
  22. data/lib/active_record/associations/has_many_association.rb +83 -22
  23. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  24. data/lib/active_record/associations/has_one_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  27. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/preloader/association.rb +14 -11
  30. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  31. data/lib/active_record/associations/singular_association.rb +17 -2
  32. data/lib/active_record/associations/through_association.rb +5 -12
  33. data/lib/active_record/attribute.rb +163 -0
  34. data/lib/active_record/attribute_assignment.rb +19 -11
  35. data/lib/active_record/attribute_decorators.rb +66 -0
  36. data/lib/active_record/attribute_methods.rb +56 -94
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  39. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  40. data/lib/active_record/attribute_methods/query.rb +1 -1
  41. data/lib/active_record/attribute_methods/read.rb +22 -59
  42. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  44. data/lib/active_record/attribute_methods/write.rb +9 -24
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attribute_set/builder.rb +106 -0
  47. data/lib/active_record/attributes.rb +147 -0
  48. data/lib/active_record/autosave_association.rb +19 -12
  49. data/lib/active_record/base.rb +13 -24
  50. data/lib/active_record/callbacks.rb +6 -6
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  55. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  63. data/lib/active_record/connection_adapters/column.rb +29 -240
  64. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  68. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  71. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  100. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  101. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  102. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  104. data/lib/active_record/connection_handling.rb +1 -1
  105. data/lib/active_record/core.rb +163 -39
  106. data/lib/active_record/counter_cache.rb +60 -6
  107. data/lib/active_record/enum.rb +9 -11
  108. data/lib/active_record/errors.rb +53 -30
  109. data/lib/active_record/explain.rb +1 -1
  110. data/lib/active_record/explain_subscriber.rb +1 -1
  111. data/lib/active_record/fixtures.rb +55 -69
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +35 -10
  114. data/lib/active_record/integration.rb +4 -4
  115. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  116. data/lib/active_record/locking/optimistic.rb +46 -26
  117. data/lib/active_record/migration.rb +71 -46
  118. data/lib/active_record/migration/command_recorder.rb +19 -2
  119. data/lib/active_record/migration/join_table.rb +1 -1
  120. data/lib/active_record/model_schema.rb +52 -58
  121. data/lib/active_record/nested_attributes.rb +5 -5
  122. data/lib/active_record/no_touching.rb +1 -1
  123. data/lib/active_record/persistence.rb +46 -26
  124. data/lib/active_record/query_cache.rb +3 -3
  125. data/lib/active_record/querying.rb +10 -7
  126. data/lib/active_record/railtie.rb +18 -11
  127. data/lib/active_record/railties/databases.rake +50 -51
  128. data/lib/active_record/readonly_attributes.rb +0 -1
  129. data/lib/active_record/reflection.rb +273 -114
  130. data/lib/active_record/relation.rb +57 -25
  131. data/lib/active_record/relation/batches.rb +0 -2
  132. data/lib/active_record/relation/calculations.rb +41 -37
  133. data/lib/active_record/relation/finder_methods.rb +70 -47
  134. data/lib/active_record/relation/merger.rb +39 -29
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  137. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  138. data/lib/active_record/relation/query_methods.rb +114 -65
  139. data/lib/active_record/relation/spawn_methods.rb +3 -0
  140. data/lib/active_record/result.rb +18 -7
  141. data/lib/active_record/sanitization.rb +12 -2
  142. data/lib/active_record/schema.rb +0 -1
  143. data/lib/active_record/schema_dumper.rb +59 -28
  144. data/lib/active_record/schema_migration.rb +5 -4
  145. data/lib/active_record/scoping/default.rb +6 -4
  146. data/lib/active_record/scoping/named.rb +4 -0
  147. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  148. data/lib/active_record/statement_cache.rb +95 -10
  149. data/lib/active_record/store.rb +5 -5
  150. data/lib/active_record/tasks/database_tasks.rb +61 -6
  151. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
  152. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  153. data/lib/active_record/timestamp.rb +9 -7
  154. data/lib/active_record/transactions.rb +53 -27
  155. data/lib/active_record/type.rb +23 -0
  156. data/lib/active_record/type/big_integer.rb +13 -0
  157. data/lib/active_record/type/binary.rb +50 -0
  158. data/lib/active_record/type/boolean.rb +31 -0
  159. data/lib/active_record/type/date.rb +50 -0
  160. data/lib/active_record/type/date_time.rb +54 -0
  161. data/lib/active_record/type/decimal.rb +64 -0
  162. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  163. data/lib/active_record/type/decorator.rb +14 -0
  164. data/lib/active_record/type/float.rb +19 -0
  165. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  166. data/lib/active_record/type/integer.rb +59 -0
  167. data/lib/active_record/type/mutable.rb +16 -0
  168. data/lib/active_record/type/numeric.rb +36 -0
  169. data/lib/active_record/type/serialized.rb +62 -0
  170. data/lib/active_record/type/string.rb +40 -0
  171. data/lib/active_record/type/text.rb +11 -0
  172. data/lib/active_record/type/time.rb +26 -0
  173. data/lib/active_record/type/time_value.rb +38 -0
  174. data/lib/active_record/type/type_map.rb +64 -0
  175. data/lib/active_record/type/unsigned_integer.rb +15 -0
  176. data/lib/active_record/type/value.rb +110 -0
  177. data/lib/active_record/validations.rb +25 -19
  178. data/lib/active_record/validations/associated.rb +5 -3
  179. data/lib/active_record/validations/presence.rb +5 -3
  180. data/lib/active_record/validations/uniqueness.rb +25 -29
  181. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  182. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +66 -11
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -99,7 +99,7 @@ module ActiveRecord
99
99
  self.local_stored_attributes[store_attribute] |= keys
100
100
  end
101
101
 
102
- def _store_accessors_module
102
+ def _store_accessors_module # :nodoc:
103
103
  @_store_accessors_module ||= begin
104
104
  mod = Module.new
105
105
  include mod
@@ -129,10 +129,10 @@ module ActiveRecord
129
129
 
130
130
  private
131
131
  def store_accessor_for(store_attribute)
132
- @column_types[store_attribute.to_s].accessor
132
+ type_for_attribute(store_attribute.to_s).accessor
133
133
  end
134
134
 
135
- class HashAccessor
135
+ class HashAccessor # :nodoc:
136
136
  def self.read(object, attribute, key)
137
137
  prepare(object, attribute)
138
138
  object.public_send(attribute)[key]
@@ -151,7 +151,7 @@ module ActiveRecord
151
151
  end
152
152
  end
153
153
 
154
- class StringKeyedHashAccessor < HashAccessor
154
+ class StringKeyedHashAccessor < HashAccessor # :nodoc:
155
155
  def self.read(object, attribute, key)
156
156
  super object, attribute, key.to_s
157
157
  end
@@ -161,7 +161,7 @@ module ActiveRecord
161
161
  end
162
162
  end
163
163
 
164
- class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor
164
+ class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor # :nodoc:
165
165
  def self.prepare(object, store_attribute)
166
166
  attribute = object.send(store_attribute)
167
167
  unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/string/filters'
2
+
1
3
  module ActiveRecord
2
4
  module Tasks # :nodoc:
3
5
  class DatabaseAlreadyExists < StandardError; end # :nodoc:
@@ -6,7 +8,7 @@ module ActiveRecord
6
8
  # <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
7
9
  # logic behind common tasks used to manage database and migrations.
8
10
  #
9
- # The tasks defined here are used in rake tasks provided by Active Record.
11
+ # The tasks defined here are used with Rake tasks provided by Active Record.
10
12
  #
11
13
  # In order to use DatabaseTasks, a few config values need to be set. All the needed
12
14
  # config values are set by Rails already, so it's necessary to do it only if you
@@ -14,7 +16,6 @@ module ActiveRecord
14
16
  # (in such case after configuring the database tasks, you can also use the rake tasks
15
17
  # defined in Active Record).
16
18
  #
17
- #
18
19
  # The possible config values are:
19
20
  #
20
21
  # * +env+: current environment (like Rails.env).
@@ -28,7 +29,7 @@ module ActiveRecord
28
29
  # Example usage of +DatabaseTasks+ outside Rails could look as such:
29
30
  #
30
31
  # include ActiveRecord::Tasks
31
- # DatabaseTasks.database_configuration = YAML.load(File.read('my_database_config.yml'))
32
+ # DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
32
33
  # DatabaseTasks.db_dir = 'db'
33
34
  # # other settings...
34
35
  #
@@ -59,7 +60,11 @@ module ActiveRecord
59
60
  end
60
61
 
61
62
  def fixtures_path
62
- @fixtures_path ||= File.join(root, 'test', 'fixtures')
63
+ @fixtures_path ||= if ENV['FIXTURES_PATH']
64
+ File.join(root, ENV['FIXTURES_PATH'])
65
+ else
66
+ File.join(root, 'test', 'fixtures')
67
+ end
63
68
  end
64
69
 
65
70
  def root
@@ -107,6 +112,8 @@ module ActiveRecord
107
112
  def drop(*arguments)
108
113
  configuration = arguments.first
109
114
  class_for_adapter(configuration['adapter']).new(*arguments).drop
115
+ rescue ActiveRecord::NoDatabaseError
116
+ $stderr.puts "Database '#{configuration['database']}' does not exist"
110
117
  rescue Exception => error
111
118
  $stderr.puts error, *(error.backtrace)
112
119
  $stderr.puts "Couldn't drop #{configuration['database']}"
@@ -122,6 +129,21 @@ module ActiveRecord
122
129
  }
123
130
  end
124
131
 
132
+ def migrate
133
+ raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
134
+
135
+ verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
136
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
137
+ scope = ENV['SCOPE']
138
+ verbose_was, Migration.verbose = Migration.verbose, verbose
139
+ Migrator.migrate(migrations_paths, version) do |migration|
140
+ scope.blank? || scope == migration.scope
141
+ end
142
+ ActiveRecord::Base.clear_cache!
143
+ ensure
144
+ Migration.verbose = verbose_was
145
+ end
146
+
125
147
  def charset_current(environment = env)
126
148
  charset ActiveRecord::Base.configurations[environment]
127
149
  end
@@ -144,6 +166,19 @@ module ActiveRecord
144
166
  class_for_adapter(configuration['adapter']).new(configuration).purge
145
167
  end
146
168
 
169
+ def purge_all
170
+ each_local_configuration { |configuration|
171
+ purge configuration
172
+ }
173
+ end
174
+
175
+ def purge_current(environment = env)
176
+ each_current_configuration(environment) { |configuration|
177
+ purge configuration
178
+ }
179
+ ActiveRecord::Base.establish_connection(environment.to_sym)
180
+ end
181
+
147
182
  def structure_dump(*arguments)
148
183
  configuration = arguments.first
149
184
  filename = arguments.delete_at 1
@@ -157,20 +192,34 @@ module ActiveRecord
157
192
  end
158
193
 
159
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
+
160
200
  load_schema_current(format, file)
161
201
  end
162
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
+
163
212
  # This method is the successor of +load_schema+. We should rename it
164
213
  # after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
165
214
  def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
215
+ file ||= schema_file(format)
216
+
166
217
  case format
167
218
  when :ruby
168
- file ||= File.join(db_dir, "schema.rb")
169
219
  check_schema_file(file)
170
220
  ActiveRecord::Base.establish_connection(configuration)
171
221
  load(file)
172
222
  when :sql
173
- file ||= File.join(db_dir, "structure.sql")
174
223
  check_schema_file(file)
175
224
  structure_load(configuration, file)
176
225
  else
@@ -178,6 +227,12 @@ module ActiveRecord
178
227
  end
179
228
  end
180
229
 
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)
233
+ end
234
+ end
235
+
181
236
  def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
182
237
  each_current_configuration(environment) { |configuration|
183
238
  load_schema_for configuration, format, file
@@ -31,6 +31,7 @@ module ActiveRecord
31
31
  end
32
32
  establish_connection configuration
33
33
  else
34
+ $stderr.puts error.inspect
34
35
  $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}"
35
36
  $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding']
36
37
  end
@@ -55,21 +56,20 @@ module ActiveRecord
55
56
  end
56
57
 
57
58
  def structure_dump(filename)
58
- args = prepare_command_options('mysqldump')
59
+ args = prepare_command_options
59
60
  args.concat(["--result-file", "#{filename}"])
60
61
  args.concat(["--no-data"])
61
62
  args.concat(["#{configuration['database']}"])
62
- unless Kernel.system(*args)
63
- $stderr.puts "Could not dump the database structure. "\
64
- "Make sure `mysqldump` is in your PATH and check the command output for warnings."
65
- end
63
+
64
+ run_cmd('mysqldump', args, 'dumping')
66
65
  end
67
66
 
68
67
  def structure_load(filename)
69
- args = prepare_command_options('mysql')
68
+ args = prepare_command_options
70
69
  args.concat(['--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}])
71
70
  args.concat(["--database", "#{configuration['database']}"])
72
- Kernel.system(*args)
71
+
72
+ run_cmd('mysql', args, 'loading')
73
73
  end
74
74
 
75
75
  private
@@ -124,20 +124,35 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
124
124
  end
125
125
 
126
126
  def root_password
127
- $stdout.print "Please provide the root password for your mysql installation\n>"
127
+ $stdout.print "Please provide the root password for your MySQL installation\n>"
128
128
  $stdin.gets.strip
129
129
  end
130
130
 
131
- def prepare_command_options(command)
132
- args = [command]
133
- args.concat(['--user', configuration['username']]) if configuration['username']
134
- args << "--password=#{configuration['password']}" if configuration['password']
135
- args.concat(['--default-character-set', configuration['encoding']]) if configuration['encoding']
136
- configuration.slice('host', 'port', 'socket').each do |k, v|
137
- args.concat([ "--#{k}", v.to_s ]) if v
138
- end
131
+ def prepare_command_options
132
+ {
133
+ 'host' => '--host',
134
+ 'port' => '--port',
135
+ 'socket' => '--socket',
136
+ 'username' => '--user',
137
+ 'password' => '--password',
138
+ 'encoding' => '--default-character-set',
139
+ 'sslca' => '--ssl-ca',
140
+ 'sslcert' => '--ssl-cert',
141
+ 'sslcapath' => '--ssl-capath',
142
+ 'sslcipher' => '--ssl-cipher',
143
+ 'sslkey' => '--ssl-key'
144
+ }.map { |opt, arg| "#{arg}=#{configuration[opt]}" if configuration[opt] }.compact
145
+ end
146
+
147
+ def run_cmd(cmd, args, action)
148
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
149
+ end
139
150
 
140
- args
151
+ def run_cmd_error(cmd, args, action)
152
+ msg = "failed to execute:\n"
153
+ msg << "#{cmd}"
154
+ 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
+ msg
141
156
  end
142
157
  end
143
158
  end
@@ -1,5 +1,3 @@
1
- require 'shellwords'
2
-
3
1
  module ActiveRecord
4
2
  module Tasks # :nodoc:
5
3
  class PostgreSQLDatabaseTasks # :nodoc:
@@ -46,20 +44,22 @@ module ActiveRecord
46
44
 
47
45
  def structure_dump(filename)
48
46
  set_psql_env
47
+ args = ['-s', '-x', '-O', '-f', filename]
49
48
  search_path = configuration['schema_search_path']
50
49
  unless search_path.blank?
51
- search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ")
50
+ args += search_path.split(',').map do |part|
51
+ "--schema=#{part.strip}"
52
+ end
52
53
  end
53
-
54
- command = "pg_dump -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(configuration['database'])}"
55
- raise 'Error dumping database' unless Kernel.system(command)
56
-
57
- File.open(filename, "a") { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" }
54
+ args << configuration['database']
55
+ run_cmd('pg_dump', args, 'dumping')
56
+ File.open(filename, "a") { |f| f << "SET search_path TO #{connection.schema_search_path};\n\n" }
58
57
  end
59
58
 
60
59
  def structure_load(filename)
61
60
  set_psql_env
62
- Kernel.system("psql -q -f #{Shellwords.escape(filename)} #{configuration['database']}")
61
+ args = [ '-q', '-f', filename, configuration['database'] ]
62
+ run_cmd('psql', args, 'loading')
63
63
  end
64
64
 
65
65
  private
@@ -85,6 +85,17 @@ module ActiveRecord
85
85
  ENV['PGPASSWORD'] = configuration['password'].to_s if configuration['password']
86
86
  ENV['PGUSER'] = configuration['username'].to_s if configuration['username']
87
87
  end
88
+
89
+ def run_cmd(cmd, args, action)
90
+ fail run_cmd_error(cmd, args, action) unless Kernel.system(cmd, *args)
91
+ end
92
+
93
+ def run_cmd_error(cmd, args, action)
94
+ msg = "failed to execute:\n"
95
+ msg << "#{cmd} #{args.join(' ')}\n\n"
96
+ 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"
97
+ msg
98
+ end
88
99
  end
89
100
  end
90
101
  end
@@ -1,4 +1,3 @@
1
-
2
1
  module ActiveRecord
3
2
  # = Active Record Timestamp
4
3
  #
@@ -48,8 +47,9 @@ module ActiveRecord
48
47
  current_time = current_time_from_proper_timezone
49
48
 
50
49
  all_timestamp_attributes.each do |column|
51
- if respond_to?(column) && respond_to?("#{column}=") && self.send(column).nil?
52
- write_attribute(column.to_s, current_time)
50
+ column = column.to_s
51
+ if has_attribute?(column) && !attribute_present?(column)
52
+ write_attribute(column, current_time)
53
53
  end
54
54
  end
55
55
  end
@@ -99,9 +99,11 @@ module ActiveRecord
99
99
  end
100
100
 
101
101
  def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update)
102
- if (timestamps = timestamp_names.map { |attr| self[attr] }.compact).present?
103
- timestamps.map { |ts| ts.to_time }.max
104
- end
102
+ timestamp_names
103
+ .map { |attr| self[attr] }
104
+ .compact
105
+ .map(&:to_time)
106
+ .max
105
107
  end
106
108
 
107
109
  def current_time_from_proper_timezone
@@ -112,7 +114,7 @@ module ActiveRecord
112
114
  def clear_timestamp_attributes
113
115
  all_timestamp_attributes_in_model.each do |attribute_name|
114
116
  self[attribute_name] = nil
115
- changed_attributes.delete(attribute_name)
117
+ clear_attribute_changes([attribute_name])
116
118
  end
117
119
  end
118
120
  end
@@ -1,15 +1,27 @@
1
- require 'thread'
2
-
3
1
  module ActiveRecord
4
2
  # See ActiveRecord::Transactions::ClassMethods for documentation.
5
3
  module Transactions
6
4
  extend ActiveSupport::Concern
5
+ #:nodoc:
7
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"
8
17
 
9
18
  included do
10
19
  define_callbacks :commit, :rollback,
11
20
  terminator: ->(_, result) { result == false },
12
21
  scope: [:kind, :name]
22
+
23
+ mattr_accessor :raise_in_transactional_callbacks, instance_writer: false
24
+ self.raise_in_transactional_callbacks = false
13
25
  end
14
26
 
15
27
  # = Active Record Transactions
@@ -225,6 +237,9 @@ module ActiveRecord
225
237
  def after_commit(*args, &block)
226
238
  set_options_for_callbacks!(args)
227
239
  set_callback(:commit, :after, *args, &block)
240
+ unless ActiveRecord::Base.raise_in_transactional_callbacks
241
+ ActiveSupport::Deprecation.warn(CALLBACK_WARN_MESSAGE)
242
+ end
228
243
  end
229
244
 
230
245
  # This callback is called after a create, update, or destroy are rolled back.
@@ -233,6 +248,9 @@ module ActiveRecord
233
248
  def after_rollback(*args, &block)
234
249
  set_options_for_callbacks!(args)
235
250
  set_callback(:rollback, :after, *args, &block)
251
+ unless ActiveRecord::Base.raise_in_transactional_callbacks
252
+ ActiveSupport::Deprecation.warn(CALLBACK_WARN_MESSAGE)
253
+ end
236
254
  end
237
255
 
238
256
  private
@@ -292,16 +310,16 @@ module ActiveRecord
292
310
  #
293
311
  # Ensure that it is not called if the object was never persisted (failed create),
294
312
  # but call it after the commit of a destroyed object.
295
- def committed! #:nodoc:
296
- run_callbacks :commit if destroyed? || persisted?
313
+ def committed!(should_run_callbacks = true) #:nodoc:
314
+ _run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
297
315
  ensure
298
- @_start_transaction_state.clear
316
+ force_clear_transaction_record_state
299
317
  end
300
318
 
301
319
  # Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record
302
320
  # state should be rolled back to the beginning or just to the last savepoint.
303
- def rolledback!(force_restore_state = false) #:nodoc:
304
- run_callbacks :rollback
321
+ def rolledback!(force_restore_state = false, should_run_callbacks = true) #:nodoc:
322
+ _run_rollback_callbacks if should_run_callbacks
305
323
  ensure
306
324
  restore_transaction_record_state(force_restore_state)
307
325
  clear_transaction_record_state
@@ -310,9 +328,13 @@ module ActiveRecord
310
328
  # Add the record to the current transaction so that the +after_rollback+ and +after_commit+ callbacks
311
329
  # can be called.
312
330
  def add_to_transaction
313
- if self.class.connection.add_transaction_record(self)
314
- remember_transaction_record_state
331
+ if has_transactional_callbacks?
332
+ self.class.connection.add_transaction_record(self)
333
+ else
334
+ sync_with_transaction_state
335
+ set_transaction_state(self.class.connection.transaction_state)
315
336
  end
337
+ remember_transaction_record_state
316
338
  end
317
339
 
318
340
  # Executes +method+ within a transaction and captures its return value as a
@@ -328,34 +350,41 @@ module ActiveRecord
328
350
  begin
329
351
  status = yield
330
352
  rescue ActiveRecord::Rollback
331
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
353
+ clear_transaction_record_state
332
354
  status = nil
333
355
  end
334
356
 
335
357
  raise ActiveRecord::Rollback unless status
336
358
  end
337
359
  status
360
+ ensure
361
+ if @transaction_state && @transaction_state.committed?
362
+ clear_transaction_record_state
363
+ end
338
364
  end
339
365
 
340
366
  protected
341
367
 
342
368
  # Save the new record state and id of a record so it can be restored later if a transaction fails.
343
369
  def remember_transaction_record_state #:nodoc:
344
- @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
345
- unless @_start_transaction_state.include?(:new_record)
346
- @_start_transaction_state[:new_record] = @new_record
347
- end
348
- unless @_start_transaction_state.include?(:destroyed)
349
- @_start_transaction_state[:destroyed] = @destroyed
350
- end
370
+ @_start_transaction_state[:id] = id
371
+ @_start_transaction_state.reverse_merge!(
372
+ new_record: @new_record,
373
+ destroyed: @destroyed,
374
+ frozen?: frozen?,
375
+ )
351
376
  @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
352
- @_start_transaction_state[:frozen?] = @attributes.frozen?
353
377
  end
354
378
 
355
379
  # Clear the new record state and id of a record.
356
380
  def clear_transaction_record_state #:nodoc:
357
381
  @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
358
- @_start_transaction_state.clear if @_start_transaction_state[:level] < 1
382
+ force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
383
+ end
384
+
385
+ # Force to clear the transaction record state.
386
+ def force_clear_transaction_record_state #:nodoc:
387
+ @_start_transaction_state.clear
359
388
  end
360
389
 
361
390
  # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
@@ -364,17 +393,14 @@ module ActiveRecord
364
393
  transaction_level = (@_start_transaction_state[:level] || 0) - 1
365
394
  if transaction_level < 1 || force
366
395
  restore_state = @_start_transaction_state
367
- was_frozen = restore_state[:frozen?]
368
- @attributes = @attributes.dup if @attributes.frozen?
396
+ thaw
369
397
  @new_record = restore_state[:new_record]
370
398
  @destroyed = restore_state[:destroyed]
371
- if restore_state.has_key?(:id)
372
- write_attribute(self.class.primary_key, restore_state[:id])
373
- else
374
- @attributes.delete(self.class.primary_key)
375
- @attributes_cache.delete(self.class.primary_key)
399
+ pk = self.class.primary_key
400
+ if pk && read_attribute(pk) != restore_state[:id]
401
+ write_attribute(pk, restore_state[:id])
376
402
  end
377
- @attributes.freeze if was_frozen
403
+ freeze if restore_state[:frozen?]
378
404
  end
379
405
  end
380
406
  end