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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1162 -1801
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +56 -94
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +71 -46
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +4 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- 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
|
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
|
-
|
63
|
-
|
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
|
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
|
-
|
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
|
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
|
132
|
-
|
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
|
-
|
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
|
-
|
50
|
+
args += search_path.split(',').map do |part|
|
51
|
+
"--schema=#{part.strip}"
|
52
|
+
end
|
52
53
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
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
|
-
|
52
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
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
|
-
|
313
|
+
def committed!(should_run_callbacks = true) #:nodoc:
|
314
|
+
_run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
|
297
315
|
ensure
|
298
|
-
|
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
|
-
|
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
|
314
|
-
|
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
|
-
|
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
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
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
|
-
|
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
|
-
|
368
|
-
@attributes = @attributes.dup if @attributes.frozen?
|
396
|
+
thaw
|
369
397
|
@new_record = restore_state[:new_record]
|
370
398
|
@destroyed = restore_state[:destroyed]
|
371
|
-
|
372
|
-
|
373
|
-
|
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
|
-
|
403
|
+
freeze if restore_state[:frozen?]
|
378
404
|
end
|
379
405
|
end
|
380
406
|
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,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
|