activerecord 4.1.16 → 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 -1801
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +15 -8
  5. data/lib/active_record/association_relation.rb +13 -0
  6. data/lib/active_record/associations/alias_tracker.rb +3 -12
  7. data/lib/active_record/associations/association.rb +16 -4
  8. data/lib/active_record/associations/association_scope.rb +83 -38
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  14. data/lib/active_record/associations/builder/has_many.rb +1 -1
  15. data/lib/active_record/associations/builder/has_one.rb +2 -2
  16. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  17. data/lib/active_record/associations/collection_association.rb +63 -27
  18. data/lib/active_record/associations/collection_proxy.rb +29 -35
  19. data/lib/active_record/associations/foreign_association.rb +11 -0
  20. data/lib/active_record/associations/has_many_association.rb +83 -22
  21. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  22. data/lib/active_record/associations/has_one_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  24. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/preloader/association.rb +14 -11
  27. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/singular_association.rb +17 -2
  30. data/lib/active_record/associations/through_association.rb +5 -12
  31. data/lib/active_record/associations.rb +158 -49
  32. data/lib/active_record/attribute.rb +163 -0
  33. data/lib/active_record/attribute_assignment.rb +19 -11
  34. data/lib/active_record/attribute_decorators.rb +66 -0
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  37. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  38. data/lib/active_record/attribute_methods/query.rb +1 -1
  39. data/lib/active_record/attribute_methods/read.rb +22 -59
  40. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  42. data/lib/active_record/attribute_methods/write.rb +9 -24
  43. data/lib/active_record/attribute_methods.rb +56 -94
  44. data/lib/active_record/attribute_set/builder.rb +106 -0
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attributes.rb +147 -0
  47. data/lib/active_record/autosave_association.rb +19 -12
  48. data/lib/active_record/base.rb +13 -24
  49. data/lib/active_record/callbacks.rb +6 -6
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  62. data/lib/active_record/connection_adapters/column.rb +29 -240
  63. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  64. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  65. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  66. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  69. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  98. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  101. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  102. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  103. data/lib/active_record/connection_handling.rb +1 -1
  104. data/lib/active_record/core.rb +163 -39
  105. data/lib/active_record/counter_cache.rb +60 -6
  106. data/lib/active_record/enum.rb +9 -11
  107. data/lib/active_record/errors.rb +53 -30
  108. data/lib/active_record/explain.rb +1 -1
  109. data/lib/active_record/explain_subscriber.rb +1 -1
  110. data/lib/active_record/fixtures.rb +55 -69
  111. data/lib/active_record/gem_version.rb +4 -4
  112. data/lib/active_record/inheritance.rb +35 -10
  113. data/lib/active_record/integration.rb +4 -4
  114. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  115. data/lib/active_record/locking/optimistic.rb +46 -26
  116. data/lib/active_record/migration/command_recorder.rb +19 -2
  117. data/lib/active_record/migration/join_table.rb +1 -1
  118. data/lib/active_record/migration.rb +71 -46
  119. data/lib/active_record/model_schema.rb +52 -58
  120. data/lib/active_record/nested_attributes.rb +5 -5
  121. data/lib/active_record/no_touching.rb +1 -1
  122. data/lib/active_record/persistence.rb +46 -26
  123. data/lib/active_record/query_cache.rb +3 -3
  124. data/lib/active_record/querying.rb +10 -7
  125. data/lib/active_record/railtie.rb +18 -11
  126. data/lib/active_record/railties/databases.rake +50 -51
  127. data/lib/active_record/readonly_attributes.rb +0 -1
  128. data/lib/active_record/reflection.rb +273 -114
  129. data/lib/active_record/relation/batches.rb +0 -2
  130. data/lib/active_record/relation/calculations.rb +41 -37
  131. data/lib/active_record/relation/finder_methods.rb +70 -47
  132. data/lib/active_record/relation/merger.rb +39 -29
  133. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  134. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/query_methods.rb +114 -65
  137. data/lib/active_record/relation/spawn_methods.rb +3 -0
  138. data/lib/active_record/relation.rb +57 -25
  139. data/lib/active_record/result.rb +18 -7
  140. data/lib/active_record/sanitization.rb +12 -2
  141. data/lib/active_record/schema.rb +0 -1
  142. data/lib/active_record/schema_dumper.rb +59 -28
  143. data/lib/active_record/schema_migration.rb +5 -4
  144. data/lib/active_record/scoping/default.rb +6 -4
  145. data/lib/active_record/scoping/named.rb +4 -0
  146. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  147. data/lib/active_record/statement_cache.rb +95 -10
  148. data/lib/active_record/store.rb +5 -5
  149. data/lib/active_record/tasks/database_tasks.rb +61 -6
  150. data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
  151. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  152. data/lib/active_record/timestamp.rb +9 -7
  153. data/lib/active_record/transactions.rb +53 -27
  154. data/lib/active_record/type/big_integer.rb +13 -0
  155. data/lib/active_record/type/binary.rb +50 -0
  156. data/lib/active_record/type/boolean.rb +31 -0
  157. data/lib/active_record/type/date.rb +50 -0
  158. data/lib/active_record/type/date_time.rb +54 -0
  159. data/lib/active_record/type/decimal.rb +64 -0
  160. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  161. data/lib/active_record/type/decorator.rb +14 -0
  162. data/lib/active_record/type/float.rb +19 -0
  163. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  164. data/lib/active_record/type/integer.rb +59 -0
  165. data/lib/active_record/type/mutable.rb +16 -0
  166. data/lib/active_record/type/numeric.rb +36 -0
  167. data/lib/active_record/type/serialized.rb +62 -0
  168. data/lib/active_record/type/string.rb +40 -0
  169. data/lib/active_record/type/text.rb +11 -0
  170. data/lib/active_record/type/time.rb +26 -0
  171. data/lib/active_record/type/time_value.rb +38 -0
  172. data/lib/active_record/type/type_map.rb +64 -0
  173. data/lib/active_record/type/unsigned_integer.rb +15 -0
  174. data/lib/active_record/type/value.rb +110 -0
  175. data/lib/active_record/type.rb +23 -0
  176. data/lib/active_record/validations/associated.rb +5 -3
  177. data/lib/active_record/validations/presence.rb +5 -3
  178. data/lib/active_record/validations/uniqueness.rb +25 -29
  179. data/lib/active_record/validations.rb +25 -19
  180. data/lib/active_record.rb +4 -0
  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
@@ -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,12 +124,12 @@ 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 = {
131
+ def prepare_command_options
132
+ {
133
133
  'host' => '--host',
134
134
  'port' => '--port',
135
135
  'socket' => '--socket',
@@ -142,8 +142,17 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
142
142
  'sslcipher' => '--ssl-cipher',
143
143
  'sslkey' => '--ssl-key'
144
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
145
150
 
146
- [command, *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
147
156
  end
148
157
  end
149
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
@@ -0,0 +1,13 @@
1
+ require 'active_record/type/integer'
2
+
3
+ module ActiveRecord
4
+ module Type
5
+ class BigInteger < Integer # :nodoc:
6
+ private
7
+
8
+ def max_value
9
+ ::Float::INFINITY
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Binary < Value # :nodoc:
4
+ def type
5
+ :binary
6
+ end
7
+
8
+ def binary?
9
+ true
10
+ end
11
+
12
+ def type_cast(value)
13
+ if value.is_a?(Data)
14
+ value.to_s
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ def type_cast_for_database(value)
21
+ return if value.nil?
22
+ Data.new(super)
23
+ end
24
+
25
+ def changed_in_place?(raw_old_value, value)
26
+ old_value = type_cast_from_database(raw_old_value)
27
+ old_value != value
28
+ end
29
+
30
+ class Data # :nodoc:
31
+ def initialize(value)
32
+ @value = value.to_s
33
+ end
34
+
35
+ def to_s
36
+ @value
37
+ end
38
+ alias_method :to_str, :to_s
39
+
40
+ def hex
41
+ @value.unpack('H*')[0]
42
+ end
43
+
44
+ def ==(other)
45
+ other == to_s || super
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Boolean < Value # :nodoc:
4
+ def type
5
+ :boolean
6
+ end
7
+
8
+ private
9
+
10
+ def cast_value(value)
11
+ if value == ''
12
+ nil
13
+ elsif ConnectionAdapters::Column::TRUE_VALUES.include?(value)
14
+ true
15
+ else
16
+ if !ConnectionAdapters::Column::FALSE_VALUES.include?(value)
17
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
18
+ You attempted to assign a value which is not explicitly `true` or `false`
19
+ (#{value.inspect})
20
+ to a boolean column. Currently this value casts to `false`. This will
21
+ change to match Ruby's semantics, and will cast to `true` in Rails 5.
22
+ If you would like to maintain the current behavior, you should
23
+ explicitly handle the values you would like cast to `false`.
24
+ MSG
25
+ end
26
+ false
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Date < Value # :nodoc:
4
+ def type
5
+ :date
6
+ end
7
+
8
+ def klass
9
+ ::Date
10
+ end
11
+
12
+ def type_cast_for_database(value)
13
+ type_cast(value)
14
+ end
15
+
16
+ def type_cast_for_schema(value)
17
+ "'#{value.to_s(:db)}'"
18
+ end
19
+
20
+ private
21
+
22
+ def cast_value(value)
23
+ if value.is_a?(::String)
24
+ return if value.empty?
25
+ fast_string_to_date(value) || fallback_string_to_date(value)
26
+ elsif value.respond_to?(:to_date)
27
+ value.to_date
28
+ else
29
+ value
30
+ end
31
+ end
32
+
33
+ def fast_string_to_date(string)
34
+ if string =~ ConnectionAdapters::Column::Format::ISO_DATE
35
+ new_date $1.to_i, $2.to_i, $3.to_i
36
+ end
37
+ end
38
+
39
+ def fallback_string_to_date(string)
40
+ new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
41
+ end
42
+
43
+ def new_date(year, mon, mday)
44
+ if year && year != 0
45
+ ::Date.new(year, mon, mday) rescue nil
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,54 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class DateTime < Value # :nodoc:
4
+ include TimeValue
5
+
6
+ def type
7
+ :datetime
8
+ end
9
+
10
+ def type_cast_for_database(value)
11
+ return super unless value.acts_like?(:time)
12
+
13
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
14
+
15
+ if value.respond_to?(zone_conversion_method)
16
+ value = value.send(zone_conversion_method)
17
+ end
18
+
19
+ return value unless has_precision?
20
+
21
+ result = value.to_s(:db)
22
+ if value.respond_to?(:usec) && (1..6).cover?(precision)
23
+ "#{result}.#{sprintf("%0#{precision}d", value.usec / 10 ** (6 - precision))}"
24
+ else
25
+ result
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ alias has_precision? precision
32
+
33
+ def cast_value(string)
34
+ return string unless string.is_a?(::String)
35
+ return if string.empty?
36
+
37
+ fast_string_to_time(string) || fallback_string_to_time(string)
38
+ end
39
+
40
+ # '0.123456' -> 123456
41
+ # '1.123456' -> 123456
42
+ def microseconds(time)
43
+ time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
44
+ end
45
+
46
+ def fallback_string_to_time(string)
47
+ time_hash = ::Date._parse(string)
48
+ time_hash[:sec_fraction] = microseconds(time_hash)
49
+
50
+ new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset))
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,64 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Decimal < Value # :nodoc:
4
+ include Numeric
5
+
6
+ def type
7
+ :decimal
8
+ end
9
+
10
+ def type_cast_for_schema(value)
11
+ value.to_s
12
+ end
13
+
14
+ private
15
+
16
+ def cast_value(value)
17
+ casted_value = case value
18
+ when ::Float
19
+ convert_float_to_big_decimal(value)
20
+ when ::Numeric
21
+ BigDecimal(value, precision.to_i)
22
+ when ::String
23
+ begin
24
+ value.to_d
25
+ rescue ArgumentError
26
+ BigDecimal(0)
27
+ end
28
+ else
29
+ if value.respond_to?(:to_d)
30
+ value.to_d
31
+ else
32
+ cast_value(value.to_s)
33
+ end
34
+ end
35
+
36
+ apply_scale(casted_value)
37
+ end
38
+
39
+ def convert_float_to_big_decimal(value)
40
+ if precision
41
+ BigDecimal(apply_scale(value), float_precision)
42
+ else
43
+ value.to_d
44
+ end
45
+ end
46
+
47
+ def float_precision
48
+ if precision.to_i > ::Float::DIG + 1
49
+ ::Float::DIG + 1
50
+ else
51
+ precision.to_i
52
+ end
53
+ end
54
+
55
+ def apply_scale(value)
56
+ if scale
57
+ value.round(scale)
58
+ else
59
+ value
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_record/type/big_integer'
2
+
3
+ module ActiveRecord
4
+ module Type
5
+ class DecimalWithoutScale < BigInteger # :nodoc:
6
+ def type
7
+ :decimal
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module ActiveRecord
2
+ module Type
3
+ module Decorator # :nodoc:
4
+ def init_with(coder)
5
+ @subtype = coder['subtype']
6
+ __setobj__(@subtype)
7
+ end
8
+
9
+ def encode_with(coder)
10
+ coder['subtype'] = __getobj__
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Float < Value # :nodoc:
4
+ include Numeric
5
+
6
+ def type
7
+ :float
8
+ end
9
+
10
+ alias type_cast_for_database type_cast
11
+
12
+ private
13
+
14
+ def cast_value(value)
15
+ value.to_f
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class HashLookupTypeMap < TypeMap # :nodoc:
4
+ def alias_type(type, alias_type)
5
+ register_type(type) { |_, *args| lookup(alias_type, *args) }
6
+ end
7
+
8
+ def key?(key)
9
+ @mapping.key?(key)
10
+ end
11
+
12
+ def keys
13
+ @mapping.keys
14
+ end
15
+
16
+ private
17
+
18
+ def perform_fetch(type, *args, &block)
19
+ @mapping.fetch(type, block).call(type, *args)
20
+ end
21
+ end
22
+ end
23
+ end