activerecord 4.2.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1537 -789
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record/aggregations.rb +37 -23
- data/lib/active_record/association_relation.rb +16 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +23 -9
- data/lib/active_record/associations/association_scope.rb +74 -102
- data/lib/active_record/associations/belongs_to_association.rb +26 -29
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +12 -20
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -10
- data/lib/active_record/associations/collection_association.rb +61 -33
- data/lib/active_record/associations/collection_proxy.rb +81 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +15 -45
- data/lib/active_record/associations/has_one_association.rb +13 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +37 -21
- data/lib/active_record/associations/preloader/association.rb +51 -53
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/through_association.rb +27 -14
- data/lib/active_record/associations/preloader.rb +18 -8
- data/lib/active_record/associations/singular_association.rb +8 -8
- data/lib/active_record/associations/through_association.rb +22 -9
- data/lib/active_record/associations.rb +321 -212
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +79 -15
- data/lib/active_record/attribute_assignment.rb +20 -141
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
- data/lib/active_record/attribute_methods/dirty.rb +51 -81
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
- data/lib/active_record/attribute_methods/write.rb +14 -38
- data/lib/active_record/attribute_methods.rb +70 -45
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +37 -15
- data/lib/active_record/attribute_set.rb +34 -3
- data/lib/active_record/attributes.rb +199 -73
- data/lib/active_record/autosave_association.rb +73 -25
- data/lib/active_record/base.rb +35 -27
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +38 -15
- data/lib/active_record/core.rb +109 -114
- data/lib/active_record/counter_cache.rb +14 -25
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +115 -79
- data/lib/active_record/errors.rb +88 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +84 -46
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +46 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +27 -25
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +372 -114
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +71 -32
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +124 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +28 -19
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +67 -51
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +318 -139
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +80 -102
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +167 -97
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +38 -41
- data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +124 -82
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +323 -257
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -10
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +176 -115
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -17
- data/lib/active_record/scoping/default.rb +24 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +59 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
- data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +159 -67
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -41
- data/lib/active_record/type/date_time.rb +2 -38
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +21 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +29 -18
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +9 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +60 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -101
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
#
|
12
12
|
# == Usage
|
13
13
|
#
|
14
|
-
# Active
|
14
|
+
# Active Record supports optimistic locking if the +lock_version+ field is present. Each update to the
|
15
15
|
# record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice
|
16
16
|
# will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
|
17
17
|
#
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
22
22
|
# p1.save
|
23
23
|
#
|
24
24
|
# p2.first_name = "should fail"
|
25
|
-
# p2.save # Raises
|
25
|
+
# p2.save # Raises an ActiveRecord::StaleObjectError
|
26
26
|
#
|
27
27
|
# Optimistic locking will also check for stale data when objects are destroyed. Example:
|
28
28
|
#
|
@@ -32,7 +32,7 @@ module ActiveRecord
|
|
32
32
|
# p1.first_name = "Michael"
|
33
33
|
# p1.save
|
34
34
|
#
|
35
|
-
# p2.destroy # Raises
|
35
|
+
# p2.destroy # Raises an ActiveRecord::StaleObjectError
|
36
36
|
#
|
37
37
|
# You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
|
38
38
|
# or otherwise apply the business logic needed to resolve the conflict.
|
@@ -66,6 +66,15 @@ module ActiveRecord
|
|
66
66
|
send(lock_col + '=', previous_lock_value + 1)
|
67
67
|
end
|
68
68
|
|
69
|
+
def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
|
70
|
+
if locking_enabled?
|
71
|
+
# We always want to persist the locking version, even if we don't detect
|
72
|
+
# a change from the default, since the database might have no default
|
73
|
+
attribute_names |= [self.class.locking_column]
|
74
|
+
end
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
69
78
|
def _update_record(attribute_names = self.attribute_names) #:nodoc:
|
70
79
|
return super unless locking_enabled?
|
71
80
|
return 0 if attribute_names.empty?
|
@@ -80,17 +89,15 @@ module ActiveRecord
|
|
80
89
|
begin
|
81
90
|
relation = self.class.unscoped
|
82
91
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
92
|
+
affected_rows = relation.where(
|
93
|
+
self.class.primary_key => id,
|
94
|
+
lock_col => previous_lock_value,
|
95
|
+
).update_all(
|
96
|
+
attributes_for_update(attribute_names).map do |name|
|
97
|
+
[name, _read_attribute(name)]
|
98
|
+
end.to_h
|
90
99
|
)
|
91
100
|
|
92
|
-
affected_rows = self.class.connection.update stmt
|
93
|
-
|
94
101
|
unless affected_rows == 1
|
95
102
|
raise ActiveRecord::StaleObjectError.new(self, "update")
|
96
103
|
end
|
@@ -118,12 +125,8 @@ module ActiveRecord
|
|
118
125
|
relation = super
|
119
126
|
|
120
127
|
if locking_enabled?
|
121
|
-
|
122
|
-
|
123
|
-
substitute = self.class.connection.substitute_at(column)
|
124
|
-
|
125
|
-
relation = relation.where(self.class.arel_table[column_name].eq(substitute))
|
126
|
-
relation.bind_values << [column, self[column_name].to_i]
|
128
|
+
locking_column = self.class.locking_column
|
129
|
+
relation = relation.where(locking_column => _read_attribute(locking_column))
|
127
130
|
end
|
128
131
|
|
129
132
|
relation
|
@@ -141,13 +144,13 @@ module ActiveRecord
|
|
141
144
|
|
142
145
|
# Set the column to use for optimistic locking. Defaults to +lock_version+.
|
143
146
|
def locking_column=(value)
|
144
|
-
|
147
|
+
reload_schema_from_cache
|
145
148
|
@locking_column = value.to_s
|
146
149
|
end
|
147
150
|
|
148
151
|
# The version column used for optimistic locking. Defaults to +lock_version+.
|
149
152
|
def locking_column
|
150
|
-
|
153
|
+
@locking_column = DEFAULT_LOCKING_COLUMN unless defined?(@locking_column)
|
151
154
|
@locking_column
|
152
155
|
end
|
153
156
|
|
@@ -181,15 +184,14 @@ module ActiveRecord
|
|
181
184
|
end
|
182
185
|
end
|
183
186
|
|
184
|
-
class LockingType <
|
185
|
-
def
|
187
|
+
class LockingType < DelegateClass(Type::Value) # :nodoc:
|
188
|
+
def deserialize(value)
|
186
189
|
# `nil` *should* be changed to 0
|
187
190
|
super.to_i
|
188
191
|
end
|
189
192
|
|
190
|
-
def
|
191
|
-
|
192
|
-
super || old_value == 0
|
193
|
+
def serialize(value)
|
194
|
+
super.to_i
|
193
195
|
end
|
194
196
|
|
195
197
|
def init_with(coder)
|
@@ -51,7 +51,7 @@ module ActiveRecord
|
|
51
51
|
# end
|
52
52
|
#
|
53
53
|
# Database-specific information on row locking:
|
54
|
-
# MySQL: http://dev.mysql.com/doc/refman/5.
|
54
|
+
# MySQL: http://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
|
55
55
|
# PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
|
56
56
|
module Pessimistic
|
57
57
|
# Obtain a row lock on this record. Reloads the record to obtain the requested
|
@@ -20,24 +20,25 @@ module ActiveRecord
|
|
20
20
|
@odd = false
|
21
21
|
end
|
22
22
|
|
23
|
-
def render_bind(
|
24
|
-
if
|
25
|
-
if
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
def render_bind(attribute)
|
24
|
+
value = if attribute.type.binary? && attribute.value
|
25
|
+
if attribute.value.is_a?(Hash)
|
26
|
+
"<#{attribute.value_for_database.to_s.bytesize} bytes of binary data>"
|
27
|
+
else
|
28
|
+
"<#{attribute.value.bytesize} bytes of binary data>"
|
29
29
|
end
|
30
|
-
|
31
|
-
[column.name, value]
|
32
30
|
else
|
33
|
-
|
31
|
+
attribute.value_for_database
|
34
32
|
end
|
33
|
+
|
34
|
+
[attribute.name, value]
|
35
35
|
end
|
36
36
|
|
37
37
|
def sql(event)
|
38
|
-
self.class.runtime += event.duration
|
39
38
|
return unless logger.debug?
|
40
39
|
|
40
|
+
self.class.runtime += event.duration
|
41
|
+
|
41
42
|
payload = event.payload
|
42
43
|
|
43
44
|
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
|
@@ -47,23 +48,44 @@ module ActiveRecord
|
|
47
48
|
binds = nil
|
48
49
|
|
49
50
|
unless (payload[:binds] || []).empty?
|
50
|
-
binds = " " + payload[:binds].map { |
|
51
|
-
render_bind(col, v)
|
52
|
-
}.inspect
|
51
|
+
binds = " " + payload[:binds].map { |attr| render_bind(attr) }.inspect
|
53
52
|
end
|
54
53
|
|
55
|
-
|
56
|
-
|
57
|
-
sql = color(sql, nil, true)
|
58
|
-
else
|
59
|
-
name = color(name, MAGENTA, true)
|
60
|
-
end
|
54
|
+
name = colorize_payload_name(name, payload[:name])
|
55
|
+
sql = color(sql, sql_color(sql), true)
|
61
56
|
|
62
57
|
debug " #{name} #{sql}#{binds}"
|
63
58
|
end
|
64
59
|
|
65
|
-
|
66
|
-
|
60
|
+
private
|
61
|
+
|
62
|
+
def colorize_payload_name(name, payload_name)
|
63
|
+
if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
|
64
|
+
color(name, MAGENTA, true)
|
65
|
+
else
|
66
|
+
color(name, CYAN, true)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def sql_color(sql)
|
71
|
+
case sql
|
72
|
+
when /\A\s*rollback/mi
|
73
|
+
RED
|
74
|
+
when /select .*for update/mi, /\A\s*lock/mi
|
75
|
+
WHITE
|
76
|
+
when /\A\s*select/i
|
77
|
+
BLUE
|
78
|
+
when /\A\s*insert/i
|
79
|
+
GREEN
|
80
|
+
when /\A\s*update/i
|
81
|
+
YELLOW
|
82
|
+
when /\A\s*delete/i
|
83
|
+
RED
|
84
|
+
when /transaction\s*\Z/i
|
85
|
+
CYAN
|
86
|
+
else
|
87
|
+
MAGENTA
|
88
|
+
end
|
67
89
|
end
|
68
90
|
|
69
91
|
def logger
|
@@ -5,15 +5,36 @@ module ActiveRecord
|
|
5
5
|
# knows how to invert the following commands:
|
6
6
|
#
|
7
7
|
# * add_column
|
8
|
+
# * add_foreign_key
|
8
9
|
# * add_index
|
10
|
+
# * add_reference
|
9
11
|
# * add_timestamps
|
10
|
-
# *
|
12
|
+
# * change_column
|
13
|
+
# * change_column_default (must supply a :from and :to option)
|
14
|
+
# * change_column_null
|
11
15
|
# * create_join_table
|
16
|
+
# * create_table
|
17
|
+
# * disable_extension
|
18
|
+
# * drop_join_table
|
19
|
+
# * drop_table (must supply a block)
|
20
|
+
# * enable_extension
|
21
|
+
# * remove_column (must supply a type)
|
22
|
+
# * remove_columns (must specify at least one column name or more)
|
23
|
+
# * remove_foreign_key (must supply a second table)
|
24
|
+
# * remove_index
|
25
|
+
# * remove_reference
|
12
26
|
# * remove_timestamps
|
13
27
|
# * rename_column
|
14
28
|
# * rename_index
|
15
29
|
# * rename_table
|
16
30
|
class CommandRecorder
|
31
|
+
ReversibleAndIrreversibleMethods = [:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
|
32
|
+
:rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
|
33
|
+
:change_column_default, :add_reference, :remove_reference, :transaction,
|
34
|
+
:drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension,
|
35
|
+
:change_column, :execute, :remove_columns, :change_column_null,
|
36
|
+
:add_foreign_key, :remove_foreign_key
|
37
|
+
]
|
17
38
|
include JoinTable
|
18
39
|
|
19
40
|
attr_accessor :commands, :delegate, :reverting
|
@@ -41,7 +62,7 @@ module ActiveRecord
|
|
41
62
|
@reverting = !@reverting
|
42
63
|
end
|
43
64
|
|
44
|
-
#
|
65
|
+
# Record +command+. +command+ should be a method name and arguments.
|
45
66
|
# For example:
|
46
67
|
#
|
47
68
|
# recorder.record(:method_name, [:arg1, :arg2])
|
@@ -62,7 +83,12 @@ module ActiveRecord
|
|
62
83
|
# invert the +command+.
|
63
84
|
def inverse_of(command, args, &block)
|
64
85
|
method = :"invert_#{command}"
|
65
|
-
raise IrreversibleMigration unless respond_to?(method, true)
|
86
|
+
raise IrreversibleMigration, <<-MSG.strip_heredoc unless respond_to?(method, true)
|
87
|
+
This migration uses #{command}, which is not automatically reversible.
|
88
|
+
To make the migration reversible you can either:
|
89
|
+
1. Define #up and #down methods in place of the #change method.
|
90
|
+
2. Use the #reversible method to define reversible behavior.
|
91
|
+
MSG
|
66
92
|
send(method, args, &block)
|
67
93
|
end
|
68
94
|
|
@@ -70,14 +96,7 @@ module ActiveRecord
|
|
70
96
|
super || delegate.respond_to?(*args)
|
71
97
|
end
|
72
98
|
|
73
|
-
|
74
|
-
:rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
|
75
|
-
:change_column_default, :add_reference, :remove_reference, :transaction,
|
76
|
-
:drop_join_table, :drop_table, :execute_block, :enable_extension,
|
77
|
-
:change_column, :execute, :remove_columns, :change_column_null,
|
78
|
-
:add_foreign_key, :remove_foreign_key
|
79
|
-
# irreversible methods need to be here too
|
80
|
-
].each do |method|
|
99
|
+
ReversibleAndIrreversibleMethods.each do |method|
|
81
100
|
class_eval <<-EOV, __FILE__, __LINE__ + 1
|
82
101
|
def #{method}(*args, &block) # def create_table(*args, &block)
|
83
102
|
record(:"#{method}", args, &block) # record(:create_table, args, &block)
|
@@ -151,19 +170,31 @@ module ActiveRecord
|
|
151
170
|
end
|
152
171
|
|
153
172
|
def invert_remove_index(args)
|
154
|
-
table,
|
155
|
-
|
156
|
-
|
157
|
-
|
173
|
+
table, options_or_column = *args
|
174
|
+
if (options = options_or_column).is_a?(Hash)
|
175
|
+
unless options[:column]
|
176
|
+
raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
|
177
|
+
end
|
178
|
+
options = options.dup
|
179
|
+
[:add_index, [table, options.delete(:column), options]]
|
180
|
+
elsif (column = options_or_column).present?
|
181
|
+
[:add_index, [table, column]]
|
158
182
|
end
|
159
|
-
|
160
|
-
options = options.dup
|
161
|
-
[:add_index, [table, options.delete(:column), options]]
|
162
183
|
end
|
163
184
|
|
164
185
|
alias :invert_add_belongs_to :invert_add_reference
|
165
186
|
alias :invert_remove_belongs_to :invert_remove_reference
|
166
187
|
|
188
|
+
def invert_change_column_default(args)
|
189
|
+
table, column, options = *args
|
190
|
+
|
191
|
+
unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
|
192
|
+
raise ActiveRecord::IrreversibleMigration, "change_column_default is only reversible if given a :from and :to option."
|
193
|
+
end
|
194
|
+
|
195
|
+
[:change_column_default, [table, column, from: options[:to], to: options[:from]]]
|
196
|
+
end
|
197
|
+
|
167
198
|
def invert_change_column_null(args)
|
168
199
|
args[2] = !args[2]
|
169
200
|
[:change_column_null, args]
|
@@ -184,6 +215,16 @@ module ActiveRecord
|
|
184
215
|
[:remove_foreign_key, [from_table, options]]
|
185
216
|
end
|
186
217
|
|
218
|
+
def invert_remove_foreign_key(args)
|
219
|
+
from_table, to_table, remove_options = args
|
220
|
+
raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil? || to_table.is_a?(Hash)
|
221
|
+
|
222
|
+
reversed_args = [from_table, to_table]
|
223
|
+
reversed_args << remove_options if remove_options
|
224
|
+
|
225
|
+
[:add_foreign_key, reversed_args]
|
226
|
+
end
|
227
|
+
|
187
228
|
# Forwards any missing method call to the \target.
|
188
229
|
def method_missing(method, *args, &block)
|
189
230
|
if @delegate.respond_to?(method)
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Migration
|
3
|
+
module Compatibility # :nodoc: all
|
4
|
+
def self.find(version)
|
5
|
+
version = version.to_s
|
6
|
+
name = "V#{version.tr('.', '_')}"
|
7
|
+
unless const_defined?(name)
|
8
|
+
versions = constants.grep(/\AV[0-9_]+\z/).map { |s| s.to_s.delete('V').tr('_', '.').inspect }
|
9
|
+
raise ArgumentError, "Unknown migration version #{version.inspect}; expected one of #{versions.sort.join(', ')}"
|
10
|
+
end
|
11
|
+
const_get(name)
|
12
|
+
end
|
13
|
+
|
14
|
+
V5_0 = Current
|
15
|
+
|
16
|
+
module FourTwoShared
|
17
|
+
module TableDefinition
|
18
|
+
def references(*, **options)
|
19
|
+
options[:index] ||= false
|
20
|
+
super
|
21
|
+
end
|
22
|
+
alias :belongs_to :references
|
23
|
+
|
24
|
+
def timestamps(*, **options)
|
25
|
+
options[:null] = true if options[:null].nil?
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_table(table_name, options = {})
|
31
|
+
if block_given?
|
32
|
+
super(table_name, options) do |t|
|
33
|
+
class << t
|
34
|
+
prepend TableDefinition
|
35
|
+
end
|
36
|
+
yield t
|
37
|
+
end
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def change_table(table_name, options = {})
|
44
|
+
if block_given?
|
45
|
+
super(table_name, options) do |t|
|
46
|
+
class << t
|
47
|
+
prepend TableDefinition
|
48
|
+
end
|
49
|
+
yield t
|
50
|
+
end
|
51
|
+
else
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_reference(*, **options)
|
57
|
+
options[:index] ||= false
|
58
|
+
super
|
59
|
+
end
|
60
|
+
alias :add_belongs_to :add_reference
|
61
|
+
|
62
|
+
def add_timestamps(*, **options)
|
63
|
+
options[:null] = true if options[:null].nil?
|
64
|
+
super
|
65
|
+
end
|
66
|
+
|
67
|
+
def index_exists?(table_name, column_name, options = {})
|
68
|
+
column_names = Array(column_name).map(&:to_s)
|
69
|
+
options[:name] =
|
70
|
+
if options[:name].present?
|
71
|
+
options[:name].to_s
|
72
|
+
else
|
73
|
+
index_name(table_name, column: column_names)
|
74
|
+
end
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
def remove_index(table_name, options = {})
|
79
|
+
options = { column: options } unless options.is_a?(Hash)
|
80
|
+
options[:name] = index_name_for_remove(table_name, options)
|
81
|
+
super(table_name, options)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def index_name_for_remove(table_name, options = {})
|
87
|
+
index_name = index_name(table_name, options)
|
88
|
+
|
89
|
+
unless index_name_exists?(table_name, index_name, true)
|
90
|
+
if options.is_a?(Hash) && options.has_key?(:name)
|
91
|
+
options_without_column = options.dup
|
92
|
+
options_without_column.delete :column
|
93
|
+
index_name_without_column = index_name(table_name, options_without_column)
|
94
|
+
|
95
|
+
return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
|
96
|
+
end
|
97
|
+
|
98
|
+
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
99
|
+
end
|
100
|
+
|
101
|
+
index_name
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class V4_2 < V5_0
|
106
|
+
# 4.2 is defined as a module because it needs to be shared with
|
107
|
+
# Legacy. When the time comes, V5_0 should be defined straight
|
108
|
+
# in its class.
|
109
|
+
include FourTwoShared
|
110
|
+
end
|
111
|
+
|
112
|
+
module Legacy
|
113
|
+
include FourTwoShared
|
114
|
+
|
115
|
+
def migrate(*)
|
116
|
+
ActiveSupport::Deprecation.warn \
|
117
|
+
"Directly inheriting from ActiveRecord::Migration is deprecated. " \
|
118
|
+
"Please specify the Rails release the migration was written for:\n" \
|
119
|
+
"\n" \
|
120
|
+
" class #{self.class.name} < ActiveRecord::Migration[4.2]"
|
121
|
+
super
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|