activerecord 4.1.0.beta2 → 4.1.0.rc1
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 +622 -9
- data/MIT-LICENSE +1 -1
- data/lib/active_record.rb +1 -1
- data/lib/active_record/associations.rb +10 -7
- data/lib/active_record/associations/alias_tracker.rb +39 -29
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +56 -31
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -0
- data/lib/active_record/associations/builder/association.rb +6 -0
- data/lib/active_record/associations/builder/belongs_to.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +33 -9
- data/lib/active_record/associations/collection_proxy.rb +53 -5
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +8 -8
- data/lib/active_record/associations/preloader.rb +1 -1
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +28 -5
- data/lib/active_record/attribute_methods/dirty.rb +27 -4
- data/lib/active_record/attribute_methods/read.rb +1 -1
- data/lib/active_record/attribute_methods/serialization.rb +18 -0
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/base.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +16 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -8
- data/lib/active_record/connection_adapters/abstract/transaction.rb +4 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/connection_specification.rb +200 -43
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +7 -1
- data/lib/active_record/connection_adapters/mysql_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/cast.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -17
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +25 -3
- data/lib/active_record/connection_handling.rb +64 -3
- data/lib/active_record/core.rb +28 -24
- data/lib/active_record/dynamic_matchers.rb +6 -2
- data/lib/active_record/enum.rb +111 -17
- data/lib/active_record/errors.rb +12 -0
- data/lib/active_record/fixtures.rb +13 -15
- data/lib/active_record/inheritance.rb +29 -9
- data/lib/active_record/integration.rb +4 -2
- data/lib/active_record/migration.rb +20 -7
- data/lib/active_record/migration/command_recorder.rb +18 -6
- data/lib/active_record/persistence.rb +10 -5
- data/lib/active_record/querying.rb +1 -0
- data/lib/active_record/railtie.rb +11 -8
- data/lib/active_record/railties/databases.rake +24 -38
- data/lib/active_record/relation.rb +3 -2
- data/lib/active_record/relation/batches.rb +24 -9
- data/lib/active_record/relation/finder_methods.rb +100 -11
- data/lib/active_record/relation/query_methods.rb +39 -27
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/sanitization.rb +7 -5
- data/lib/active_record/scoping.rb +5 -0
- data/lib/active_record/scoping/named.rb +6 -0
- data/lib/active_record/store.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +45 -23
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/transactions.rb +7 -7
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/version.rb +1 -1
- metadata +5 -6
- data/lib/active_record/associations/join_helper.rb +0 -36
@@ -6,8 +6,12 @@ module ActiveRecord
|
|
6
6
|
# then we can remove the indirection.
|
7
7
|
|
8
8
|
def respond_to?(name, include_private = false)
|
9
|
-
|
10
|
-
|
9
|
+
if self == Base
|
10
|
+
super
|
11
|
+
else
|
12
|
+
match = Method.match(self, name)
|
13
|
+
match && match.valid? || super
|
14
|
+
end
|
11
15
|
end
|
12
16
|
|
13
17
|
private
|
data/lib/active_record/enum.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# Declare an enum attribute where the values map to integers in the database,
|
2
|
+
# Declare an enum attribute where the values map to integers in the database,
|
3
|
+
# but can be queried by name. Example:
|
3
4
|
#
|
4
5
|
# class Conversation < ActiveRecord::Base
|
5
6
|
# enum status: [ :active, :archived ]
|
@@ -18,6 +19,15 @@ module ActiveRecord
|
|
18
19
|
# # conversation.update! status: 1
|
19
20
|
# conversation.status = "archived"
|
20
21
|
#
|
22
|
+
# # conversation.update! status: nil
|
23
|
+
# conversation.status = nil
|
24
|
+
# conversation.status.nil? # => true
|
25
|
+
# conversation.status # => nil
|
26
|
+
#
|
27
|
+
# Scopes based on the allowed values of the enum field will be provided
|
28
|
+
# as well. With the above example, it will create an +active+ and +archived+
|
29
|
+
# scope.
|
30
|
+
#
|
21
31
|
# You can set the default value from the database declaration, like:
|
22
32
|
#
|
23
33
|
# create_table :conversations do |t|
|
@@ -44,46 +54,76 @@ module ActiveRecord
|
|
44
54
|
# remove unused values, the explicit +Hash+ syntax should be used.
|
45
55
|
#
|
46
56
|
# In rare circumstances you might need to access the mapping directly.
|
47
|
-
# The mappings are exposed through a
|
57
|
+
# The mappings are exposed through a class method with the pluralized attribute
|
58
|
+
# name:
|
59
|
+
#
|
60
|
+
# Conversation.statuses # => { "active" => 0, "archived" => 1 }
|
48
61
|
#
|
49
|
-
#
|
62
|
+
# Use that class method when you need to know the ordinal value of an enum:
|
50
63
|
#
|
51
|
-
#
|
64
|
+
# Conversation.where("status <> ?", Conversation.statuses[:archived])
|
52
65
|
#
|
53
|
-
#
|
66
|
+
# Where conditions on an enum attribute must use the ordinal value of an enum.
|
54
67
|
module Enum
|
68
|
+
DEFINED_ENUMS = {} # :nodoc:
|
69
|
+
|
70
|
+
def enum_mapping_for(attr_name) # :nodoc:
|
71
|
+
DEFINED_ENUMS[attr_name.to_s]
|
72
|
+
end
|
73
|
+
|
55
74
|
def enum(definitions)
|
56
75
|
klass = self
|
57
76
|
definitions.each do |name, values|
|
58
|
-
#
|
59
|
-
enum_values =
|
77
|
+
# statuses = { }
|
78
|
+
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
60
79
|
name = name.to_sym
|
61
80
|
|
81
|
+
# def self.statuses statuses end
|
82
|
+
detect_enum_conflict!(name, name.to_s.pluralize, true)
|
83
|
+
klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
|
84
|
+
|
62
85
|
_enum_methods_module.module_eval do
|
63
|
-
# def
|
86
|
+
# def status=(value) self[:status] = statuses[value] end
|
87
|
+
klass.send(:detect_enum_conflict!, name, "#{name}=")
|
64
88
|
define_method("#{name}=") { |value|
|
65
|
-
|
89
|
+
if enum_values.has_key?(value) || value.blank?
|
90
|
+
self[name] = enum_values[value]
|
91
|
+
elsif enum_values.has_value?(value)
|
92
|
+
# Assigning a value directly is not a end-user feature, hence it's not documented.
|
93
|
+
# This is used internally to make building objects from the generated scopes work
|
94
|
+
# as expected, i.e. +Conversation.archived.build.archived?+ should be true.
|
95
|
+
self[name] = value
|
96
|
+
else
|
66
97
|
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
67
98
|
end
|
68
|
-
self[name] = enum_values[value]
|
69
99
|
}
|
70
100
|
|
71
|
-
# def
|
101
|
+
# def status() statuses.key self[:status] end
|
102
|
+
klass.send(:detect_enum_conflict!, name, name)
|
72
103
|
define_method(name) { enum_values.key self[name] }
|
73
104
|
|
105
|
+
# def status_before_type_cast() statuses.key self[:status] end
|
106
|
+
klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
|
107
|
+
define_method("#{name}_before_type_cast") { enum_values.key self[name] }
|
108
|
+
|
74
109
|
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
75
110
|
pairs.each do |value, i|
|
76
111
|
enum_values[value] = i
|
77
112
|
|
78
|
-
#
|
79
|
-
klass.
|
80
|
-
|
81
|
-
# def incoming?() direction == 0 end
|
113
|
+
# def active?() status == 0 end
|
114
|
+
klass.send(:detect_enum_conflict!, name, "#{value}?")
|
82
115
|
define_method("#{value}?") { self[name] == i }
|
83
116
|
|
84
|
-
# def
|
117
|
+
# def active!() update! status: :active end
|
118
|
+
klass.send(:detect_enum_conflict!, name, "#{value}!")
|
85
119
|
define_method("#{value}!") { update! name => value }
|
120
|
+
|
121
|
+
# scope :active, -> { where status: 0 }
|
122
|
+
klass.send(:detect_enum_conflict!, name, value, true)
|
123
|
+
klass.scope value, -> { klass.where name => i }
|
86
124
|
end
|
125
|
+
|
126
|
+
DEFINED_ENUMS[name.to_s] = enum_values
|
87
127
|
end
|
88
128
|
end
|
89
129
|
end
|
@@ -91,10 +131,64 @@ module ActiveRecord
|
|
91
131
|
private
|
92
132
|
def _enum_methods_module
|
93
133
|
@_enum_methods_module ||= begin
|
94
|
-
mod = Module.new
|
134
|
+
mod = Module.new do
|
135
|
+
private
|
136
|
+
def save_changed_attribute(attr_name, value)
|
137
|
+
if (mapping = self.class.enum_mapping_for(attr_name))
|
138
|
+
if attribute_changed?(attr_name)
|
139
|
+
old = changed_attributes[attr_name]
|
140
|
+
|
141
|
+
if mapping[old] == value
|
142
|
+
changed_attributes.delete(attr_name)
|
143
|
+
end
|
144
|
+
else
|
145
|
+
old = clone_attribute_value(:read_attribute, attr_name)
|
146
|
+
|
147
|
+
if old != value
|
148
|
+
changed_attributes[attr_name] = mapping.key old
|
149
|
+
end
|
150
|
+
end
|
151
|
+
else
|
152
|
+
super
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
95
156
|
include mod
|
96
157
|
mod
|
97
158
|
end
|
98
159
|
end
|
160
|
+
|
161
|
+
ENUM_CONFLICT_MESSAGE = \
|
162
|
+
"You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
|
163
|
+
"this will generate a %{type} method \"%{method}\", which is already defined " \
|
164
|
+
"by %{source}."
|
165
|
+
|
166
|
+
def detect_enum_conflict!(enum_name, method_name, klass_method = false)
|
167
|
+
if klass_method && dangerous_class_method?(method_name)
|
168
|
+
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
|
169
|
+
enum: enum_name,
|
170
|
+
klass: self.name,
|
171
|
+
type: 'class',
|
172
|
+
method: method_name,
|
173
|
+
source: 'Active Record'
|
174
|
+
}
|
175
|
+
elsif !klass_method && dangerous_attribute_method?(method_name)
|
176
|
+
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
|
177
|
+
enum: enum_name,
|
178
|
+
klass: self.name,
|
179
|
+
type: 'instance',
|
180
|
+
method: method_name,
|
181
|
+
source: 'Active Record'
|
182
|
+
}
|
183
|
+
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|
184
|
+
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
|
185
|
+
enum: enum_name,
|
186
|
+
klass: self.name,
|
187
|
+
type: 'instance',
|
188
|
+
method: method_name,
|
189
|
+
source: 'another enum'
|
190
|
+
}
|
191
|
+
end
|
192
|
+
end
|
99
193
|
end
|
100
194
|
end
|
data/lib/active_record/errors.rb
CHANGED
@@ -94,6 +94,18 @@ module ActiveRecord
|
|
94
94
|
class PreparedStatementInvalid < ActiveRecordError
|
95
95
|
end
|
96
96
|
|
97
|
+
# Raised when a given database does not exist
|
98
|
+
class NoDatabaseError < ActiveRecordError
|
99
|
+
def initialize(message)
|
100
|
+
super extend_message(message)
|
101
|
+
end
|
102
|
+
|
103
|
+
# can be over written to add additional error information.
|
104
|
+
def extend_message(message)
|
105
|
+
message
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
97
109
|
# Raised on attempt to save stale record. Record is stale when it's being saved in another query after
|
98
110
|
# instantiation, for example, when two users edit the same wiki page and one starts editing and saves
|
99
111
|
# the page before the other.
|
@@ -462,7 +462,7 @@ module ActiveRecord
|
|
462
462
|
@class_names.delete_if { |k,klass|
|
463
463
|
unless klass.is_a? Class
|
464
464
|
klass = klass.safe_constantize
|
465
|
-
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name will be removed in Rails 4.2
|
465
|
+
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `set_fixture_class` will be removed in Rails 4.2. Use the class itself instead.")
|
466
466
|
end
|
467
467
|
!insert_class(@class_names, k, klass)
|
468
468
|
}
|
@@ -521,8 +521,16 @@ module ActiveRecord
|
|
521
521
|
connection.transaction(:requires_new => true) do
|
522
522
|
fixture_sets.each do |fs|
|
523
523
|
conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
|
524
|
-
fs.
|
525
|
-
|
524
|
+
table_rows = fs.table_rows
|
525
|
+
|
526
|
+
table_rows.keys.each do |table|
|
527
|
+
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
|
528
|
+
end
|
529
|
+
|
530
|
+
table_rows.each do |fixture_set_name, rows|
|
531
|
+
rows.each do |row|
|
532
|
+
conn.insert_fixture(row, fixture_set_name)
|
533
|
+
end
|
526
534
|
end
|
527
535
|
end
|
528
536
|
|
@@ -560,7 +568,7 @@ module ActiveRecord
|
|
560
568
|
@model_class = nil
|
561
569
|
|
562
570
|
if class_name.is_a?(String)
|
563
|
-
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name will be removed in Rails 4.2
|
571
|
+
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `FixtureSet.new` will be removed in Rails 4.2. Use the class itself instead.")
|
564
572
|
end
|
565
573
|
|
566
574
|
if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
|
@@ -594,17 +602,7 @@ module ActiveRecord
|
|
594
602
|
fixtures.size
|
595
603
|
end
|
596
604
|
|
597
|
-
|
598
|
-
table_rows = self.table_rows
|
599
|
-
|
600
|
-
table_rows.keys.map { |table|
|
601
|
-
"DELETE FROM #{conn.quote_table_name(table)}"
|
602
|
-
}.concat table_rows.flat_map { |fixture_set_name, rows|
|
603
|
-
rows.map { |row| conn.fixture_sql(row, fixture_set_name) }
|
604
|
-
}
|
605
|
-
end
|
606
|
-
|
607
|
-
# Return a hash of rows to be inserted. The key is the table, the value is
|
605
|
+
# Returns a hash of rows to be inserted. The key is the table, the value is
|
608
606
|
# a list of rows to insert to that table.
|
609
607
|
def table_rows
|
610
608
|
now = config.default_timezone == :utc ? Time.now.utc : Time.now
|
@@ -16,15 +16,19 @@ module ActiveRecord
|
|
16
16
|
# instance of the given subclass instead of the base class.
|
17
17
|
def new(*args, &block)
|
18
18
|
if abstract_class? || self == Base
|
19
|
-
raise NotImplementedError, "#{self} is an abstract class and
|
19
|
+
raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
|
20
20
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
|
22
|
+
attrs = args.first
|
23
|
+
if subclass_from_attributes?(attrs)
|
24
|
+
subclass = subclass_from_attributes(attrs)
|
25
|
+
end
|
26
|
+
|
27
|
+
if subclass
|
28
|
+
subclass.new(*args, &block)
|
29
|
+
else
|
30
|
+
super
|
25
31
|
end
|
26
|
-
# Delegate to the original .new
|
27
|
-
super
|
28
32
|
end
|
29
33
|
|
30
34
|
# Returns +true+ if this does not need STI type condition. Returns
|
@@ -45,10 +49,12 @@ module ActiveRecord
|
|
45
49
|
end
|
46
50
|
|
47
51
|
def symbolized_base_class
|
52
|
+
ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_base_class is deprecated and will be removed without replacement.")
|
48
53
|
@symbolized_base_class ||= base_class.to_s.to_sym
|
49
54
|
end
|
50
55
|
|
51
56
|
def symbolized_sti_name
|
57
|
+
ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_sti_name is deprecated and will be removed without replacement.")
|
52
58
|
@symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
|
53
59
|
end
|
54
60
|
|
@@ -124,7 +130,7 @@ module ActiveRecord
|
|
124
130
|
end
|
125
131
|
end
|
126
132
|
|
127
|
-
raise NameError
|
133
|
+
raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
|
128
134
|
end
|
129
135
|
end
|
130
136
|
|
@@ -170,7 +176,11 @@ module ActiveRecord
|
|
170
176
|
# is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
|
171
177
|
# If this is a StrongParameters hash, and access to inheritance_column is not permitted,
|
172
178
|
# this will ignore the inheritance column and return nil
|
173
|
-
def
|
179
|
+
def subclass_from_attributes?(attrs)
|
180
|
+
columns_hash.include?(inheritance_column) && attrs.is_a?(Hash)
|
181
|
+
end
|
182
|
+
|
183
|
+
def subclass_from_attributes(attrs)
|
174
184
|
subclass_name = attrs.with_indifferent_access[inheritance_column]
|
175
185
|
|
176
186
|
if subclass_name.present? && subclass_name != self.name
|
@@ -185,8 +195,18 @@ module ActiveRecord
|
|
185
195
|
end
|
186
196
|
end
|
187
197
|
|
198
|
+
def initialize_dup(other)
|
199
|
+
super
|
200
|
+
ensure_proper_type
|
201
|
+
end
|
202
|
+
|
188
203
|
private
|
189
204
|
|
205
|
+
def initialize_internals_callback
|
206
|
+
super
|
207
|
+
ensure_proper_type
|
208
|
+
end
|
209
|
+
|
190
210
|
# Sets the attribute used for single table inheritance to this class name if this is not the
|
191
211
|
# ActiveRecord::Base descendant.
|
192
212
|
# Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
|
@@ -98,8 +98,10 @@ module ActiveRecord
|
|
98
98
|
super()
|
99
99
|
else
|
100
100
|
define_method :to_param do
|
101
|
-
if (default = super()) &&
|
102
|
-
|
101
|
+
if (default = super()) &&
|
102
|
+
(result = send(method_name).to_s).present? &&
|
103
|
+
(param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
|
104
|
+
"#{default}-#{param}"
|
103
105
|
else
|
104
106
|
default
|
105
107
|
end
|
@@ -385,8 +385,21 @@ module ActiveRecord
|
|
385
385
|
attr_accessor :delegate # :nodoc:
|
386
386
|
attr_accessor :disable_ddl_transaction # :nodoc:
|
387
387
|
|
388
|
-
def check_pending!
|
389
|
-
raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?
|
388
|
+
def check_pending!(connection = Base.connection)
|
389
|
+
raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?(connection)
|
390
|
+
end
|
391
|
+
|
392
|
+
def load_schema_if_pending!
|
393
|
+
if ActiveRecord::Migrator.needs_migration?
|
394
|
+
ActiveRecord::Tasks::DatabaseTasks.load_schema
|
395
|
+
check_pending!
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def maintain_test_schema! # :nodoc:
|
400
|
+
if ActiveRecord::Base.maintain_test_schema
|
401
|
+
suppress_messages { load_schema_if_pending! }
|
402
|
+
end
|
390
403
|
end
|
391
404
|
|
392
405
|
def method_missing(name, *args, &block) # :nodoc:
|
@@ -746,7 +759,7 @@ module ActiveRecord
|
|
746
759
|
|
747
760
|
def load_migration
|
748
761
|
require(File.expand_path(filename))
|
749
|
-
name.constantize.new
|
762
|
+
name.constantize.new(name, version)
|
750
763
|
end
|
751
764
|
|
752
765
|
end
|
@@ -817,17 +830,17 @@ module ActiveRecord
|
|
817
830
|
SchemaMigration.all.map { |x| x.version.to_i }.sort
|
818
831
|
end
|
819
832
|
|
820
|
-
def current_version
|
833
|
+
def current_version(connection = Base.connection)
|
821
834
|
sm_table = schema_migrations_table_name
|
822
|
-
if
|
835
|
+
if connection.table_exists?(sm_table)
|
823
836
|
get_all_versions.max || 0
|
824
837
|
else
|
825
838
|
0
|
826
839
|
end
|
827
840
|
end
|
828
841
|
|
829
|
-
def needs_migration?
|
830
|
-
current_version < last_version
|
842
|
+
def needs_migration?(connection = Base.connection)
|
843
|
+
current_version(connection) < last_version
|
831
844
|
end
|
832
845
|
|
833
846
|
def last_version
|
@@ -74,7 +74,7 @@ module ActiveRecord
|
|
74
74
|
:rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
|
75
75
|
:change_column_default, :add_reference, :remove_reference, :transaction,
|
76
76
|
:drop_join_table, :drop_table, :execute_block, :enable_extension,
|
77
|
-
:change_column, :execute, :remove_columns, # irreversible methods need to be here too
|
77
|
+
:change_column, :execute, :remove_columns, :change_column_null # irreversible methods need to be here too
|
78
78
|
].each do |method|
|
79
79
|
class_eval <<-EOV, __FILE__, __LINE__ + 1
|
80
80
|
def #{method}(*args, &block) # def create_table(*args, &block)
|
@@ -86,7 +86,7 @@ module ActiveRecord
|
|
86
86
|
alias :remove_belongs_to :remove_reference
|
87
87
|
|
88
88
|
def change_table(table_name, options = {})
|
89
|
-
yield
|
89
|
+
yield delegate.update_table_definition(table_name, self)
|
90
90
|
end
|
91
91
|
|
92
92
|
private
|
@@ -140,7 +140,12 @@ module ActiveRecord
|
|
140
140
|
|
141
141
|
def invert_add_index(args)
|
142
142
|
table, columns, options = *args
|
143
|
-
|
143
|
+
options ||= {}
|
144
|
+
|
145
|
+
index_name = options[:name]
|
146
|
+
options_hash = index_name ? { name: index_name } : { column: columns }
|
147
|
+
|
148
|
+
[:remove_index, [table, options_hash]]
|
144
149
|
end
|
145
150
|
|
146
151
|
def invert_remove_index(args)
|
@@ -157,11 +162,18 @@ module ActiveRecord
|
|
157
162
|
alias :invert_add_belongs_to :invert_add_reference
|
158
163
|
alias :invert_remove_belongs_to :invert_remove_reference
|
159
164
|
|
165
|
+
def invert_change_column_null(args)
|
166
|
+
args[2] = !args[2]
|
167
|
+
[:change_column_null, args]
|
168
|
+
end
|
169
|
+
|
160
170
|
# Forwards any missing method call to the \target.
|
161
171
|
def method_missing(method, *args, &block)
|
162
|
-
@delegate.
|
163
|
-
|
164
|
-
|
172
|
+
if @delegate.respond_to?(method)
|
173
|
+
@delegate.send(method, *args, &block)
|
174
|
+
else
|
175
|
+
super
|
176
|
+
end
|
165
177
|
end
|
166
178
|
end
|
167
179
|
end
|