activerecord 4.2.0 → 4.2.1
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 +215 -1
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/belongs_to_association.rb +9 -5
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/collection_association.rb +17 -2
- data/lib/active_record/associations/collection_proxy.rb +5 -0
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +22 -14
- data/lib/active_record/associations/has_many_through_association.rb +2 -2
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/through_association.rb +11 -0
- data/lib/active_record/attribute.rb +15 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +5 -0
- data/lib/active_record/attribute_methods/dirty.rb +7 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +5 -1
- data/lib/active_record/attribute_set/builder.rb +11 -1
- data/lib/active_record/attributes.rb +7 -0
- data/lib/active_record/autosave_association.rb +23 -8
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +7 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +12 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +25 -7
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +38 -6
- data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +13 -6
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +23 -1
- data/lib/active_record/connection_adapters/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +6 -7
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +10 -16
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +13 -7
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/fixtures.rb +1 -1
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +16 -14
- data/lib/active_record/migration.rb +1 -1
- data/lib/active_record/nested_attributes.rb +1 -1
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +2 -1
- data/lib/active_record/railties/databases.rake +2 -2
- data/lib/active_record/reflection.rb +1 -1
- data/lib/active_record/relation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +21 -1
- data/lib/active_record/relation/predicate_builder/array_handler.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +19 -14
- data/lib/active_record/schema_dumper.rb +1 -1
- data/lib/active_record/transactions.rb +6 -8
- data/lib/active_record/type/date_time.rb +14 -3
- data/lib/active_record/type/decimal.rb +9 -1
- data/lib/active_record/type/integer.rb +9 -5
- data/lib/active_record/type/numeric.rb +1 -1
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/string.rb +4 -0
- data/lib/active_record/type/value.rb +4 -0
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -6
- metadata +8 -7
@@ -188,9 +188,9 @@ module ActiveRecord
|
|
188
188
|
|
189
189
|
if through_reflection.collection? && update_through_counter?(method)
|
190
190
|
update_counter(-count, through_reflection)
|
191
|
+
else
|
192
|
+
update_counter(-count)
|
191
193
|
end
|
192
|
-
|
193
|
-
update_counter(-count)
|
194
194
|
end
|
195
195
|
|
196
196
|
def through_records_for(record)
|
@@ -91,6 +91,17 @@ module ActiveRecord
|
|
91
91
|
raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection)
|
92
92
|
end
|
93
93
|
end
|
94
|
+
|
95
|
+
def build_record(attributes)
|
96
|
+
inverse = source_reflection.inverse_of
|
97
|
+
target = through_association.target
|
98
|
+
|
99
|
+
if inverse && target && !target.is_a?(Array)
|
100
|
+
attributes[inverse.foreign_key] = target.id
|
101
|
+
end
|
102
|
+
|
103
|
+
super(attributes)
|
104
|
+
end
|
94
105
|
end
|
95
106
|
end
|
96
107
|
end
|
@@ -51,7 +51,7 @@ module ActiveRecord
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def changed_in_place_from?(old_value)
|
54
|
-
type.changed_in_place?(old_value, value)
|
54
|
+
has_been_read? && type.changed_in_place?(old_value, value)
|
55
55
|
end
|
56
56
|
|
57
57
|
def with_value_from_user(value)
|
@@ -74,6 +74,10 @@ module ActiveRecord
|
|
74
74
|
true
|
75
75
|
end
|
76
76
|
|
77
|
+
def came_from_user?
|
78
|
+
false
|
79
|
+
end
|
80
|
+
|
77
81
|
def ==(other)
|
78
82
|
self.class == other.class &&
|
79
83
|
name == other.name &&
|
@@ -89,6 +93,12 @@ module ActiveRecord
|
|
89
93
|
end
|
90
94
|
end
|
91
95
|
|
96
|
+
private
|
97
|
+
|
98
|
+
def has_been_read?
|
99
|
+
defined?(@value)
|
100
|
+
end
|
101
|
+
|
92
102
|
class FromDatabase < Attribute # :nodoc:
|
93
103
|
def type_cast(value)
|
94
104
|
type.type_cast_from_database(value)
|
@@ -99,6 +109,10 @@ module ActiveRecord
|
|
99
109
|
def type_cast(value)
|
100
110
|
type.type_cast_from_user(value)
|
101
111
|
end
|
112
|
+
|
113
|
+
def came_from_user?
|
114
|
+
true
|
115
|
+
end
|
102
116
|
end
|
103
117
|
|
104
118
|
class WithCastValue < Attribute # :nodoc:
|
@@ -28,6 +28,7 @@ module ActiveRecord
|
|
28
28
|
|
29
29
|
included do
|
30
30
|
attribute_method_suffix "_before_type_cast"
|
31
|
+
attribute_method_suffix "_came_from_user?"
|
31
32
|
end
|
32
33
|
|
33
34
|
# Returns the value of the attribute identified by +attr_name+ before
|
@@ -66,6 +67,10 @@ module ActiveRecord
|
|
66
67
|
def attribute_before_type_cast(attribute_name)
|
67
68
|
read_attribute_before_type_cast(attribute_name)
|
68
69
|
end
|
70
|
+
|
71
|
+
def attribute_came_from_user?(attribute_name)
|
72
|
+
@attributes[attribute_name].came_from_user?
|
73
|
+
end
|
69
74
|
end
|
70
75
|
end
|
71
76
|
end
|
@@ -76,6 +76,10 @@ module ActiveRecord
|
|
76
76
|
|
77
77
|
private
|
78
78
|
|
79
|
+
def changes_include?(attr_name)
|
80
|
+
super || attribute_changed_in_place?(attr_name)
|
81
|
+
end
|
82
|
+
|
79
83
|
def calculate_changes_from_defaults
|
80
84
|
@changed_attributes = nil
|
81
85
|
self.class.column_defaults.each do |attr, orig_value|
|
@@ -104,7 +108,7 @@ module ActiveRecord
|
|
104
108
|
end
|
105
109
|
|
106
110
|
def save_changed_attribute(attr, old_value)
|
107
|
-
if
|
111
|
+
if attribute_changed_by_setter?(attr)
|
108
112
|
clear_attribute_changes(attr) unless _field_changed?(attr, old_value)
|
109
113
|
else
|
110
114
|
set_attribute_was(attr, old_value) if _field_changed?(attr, old_value)
|
@@ -130,7 +134,7 @@ module ActiveRecord
|
|
130
134
|
# Serialized attributes should always be written in case they've been
|
131
135
|
# changed in place.
|
132
136
|
def keys_for_partial_write
|
133
|
-
changed
|
137
|
+
changed & persistable_attribute_names
|
134
138
|
end
|
135
139
|
|
136
140
|
def _field_changed?(attr, old_value)
|
@@ -161,7 +165,7 @@ module ActiveRecord
|
|
161
165
|
end
|
162
166
|
|
163
167
|
def store_original_raw_attribute(attr_name)
|
164
|
-
original_raw_attributes[attr_name] = @attributes[attr_name].value_for_database
|
168
|
+
original_raw_attributes[attr_name] = @attributes[attr_name].value_for_database rescue nil
|
165
169
|
end
|
166
170
|
|
167
171
|
def store_original_raw_attributes
|
@@ -12,7 +12,11 @@ module ActiveRecord
|
|
12
12
|
if value.is_a?(Array)
|
13
13
|
value.map { |v| type_cast_from_user(v) }
|
14
14
|
elsif value.respond_to?(:in_time_zone)
|
15
|
-
|
15
|
+
begin
|
16
|
+
value.in_time_zone || super
|
17
|
+
rescue ArgumentError
|
18
|
+
nil
|
19
|
+
end
|
16
20
|
end
|
17
21
|
end
|
18
22
|
|
@@ -20,7 +20,7 @@ module ActiveRecord
|
|
20
20
|
end
|
21
21
|
|
22
22
|
class LazyAttributeHash # :nodoc:
|
23
|
-
delegate :
|
23
|
+
delegate :transform_values, to: :materialize
|
24
24
|
|
25
25
|
def initialize(types, values, additional_types)
|
26
26
|
@types = types
|
@@ -54,6 +54,16 @@ module ActiveRecord
|
|
54
54
|
super
|
55
55
|
end
|
56
56
|
|
57
|
+
def select
|
58
|
+
keys = types.keys | values.keys | delegate_hash.keys
|
59
|
+
keys.each_with_object({}) do |key, hash|
|
60
|
+
attribute = self[key]
|
61
|
+
if yield(key, attribute)
|
62
|
+
hash[key] = attribute
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
57
67
|
protected
|
58
68
|
|
59
69
|
attr_reader :types, :values, :additional_types, :delegate_hash
|
@@ -9,6 +9,8 @@ module ActiveRecord
|
|
9
9
|
class_attribute :user_provided_defaults, instance_accessor: false # :internal:
|
10
10
|
self.user_provided_columns = {}
|
11
11
|
self.user_provided_defaults = {}
|
12
|
+
|
13
|
+
delegate :persistable_attribute_names, to: :class
|
12
14
|
end
|
13
15
|
|
14
16
|
module ClassMethods # :nodoc:
|
@@ -96,6 +98,10 @@ module ActiveRecord
|
|
96
98
|
@columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
|
97
99
|
end
|
98
100
|
|
101
|
+
def persistable_attribute_names # :nodoc:
|
102
|
+
@persistable_attribute_names ||= connection.schema_cache.columns_hash(table_name).keys
|
103
|
+
end
|
104
|
+
|
99
105
|
def reset_column_information # :nodoc:
|
100
106
|
super
|
101
107
|
clear_caches_calculated_from_columns
|
@@ -129,6 +135,7 @@ module ActiveRecord
|
|
129
135
|
@columns_hash = nil
|
130
136
|
@content_columns = nil
|
131
137
|
@default_attributes = nil
|
138
|
+
@persistable_attribute_names = nil
|
132
139
|
end
|
133
140
|
|
134
141
|
def raw_default_values
|
@@ -177,10 +177,8 @@ module ActiveRecord
|
|
177
177
|
# before actually defining them.
|
178
178
|
def add_autosave_association_callbacks(reflection)
|
179
179
|
save_method = :"autosave_associated_records_for_#{reflection.name}"
|
180
|
-
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
181
|
-
collection = reflection.collection?
|
182
180
|
|
183
|
-
if collection
|
181
|
+
if reflection.collection?
|
184
182
|
before_save :before_save_collection_association
|
185
183
|
|
186
184
|
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
|
@@ -204,8 +202,18 @@ module ActiveRecord
|
|
204
202
|
before_save save_method
|
205
203
|
end
|
206
204
|
|
205
|
+
define_autosave_validation_callbacks(reflection)
|
206
|
+
end
|
207
|
+
|
208
|
+
def define_autosave_validation_callbacks(reflection)
|
209
|
+
validation_method = :"validate_associated_records_for_#{reflection.name}"
|
207
210
|
if reflection.validate? && !method_defined?(validation_method)
|
208
|
-
|
211
|
+
if reflection.collection?
|
212
|
+
method = :validate_collection_association
|
213
|
+
else
|
214
|
+
method = :validate_single_association
|
215
|
+
end
|
216
|
+
|
209
217
|
define_non_cyclic_method(validation_method) { send(method, reflection) }
|
210
218
|
validate validation_method
|
211
219
|
end
|
@@ -272,11 +280,18 @@ module ActiveRecord
|
|
272
280
|
# go through nested autosave associations that are loaded in memory (without loading
|
273
281
|
# any new ones), and return true if is changed for autosave
|
274
282
|
def nested_records_changed_for_autosave?
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
283
|
+
@_nested_records_changed_for_autosave_already_called ||= false
|
284
|
+
return false if @_nested_records_changed_for_autosave_already_called
|
285
|
+
begin
|
286
|
+
@_nested_records_changed_for_autosave_already_called = true
|
287
|
+
self.class._reflections.values.any? do |reflection|
|
288
|
+
if reflection.options[:autosave]
|
289
|
+
association = association_instance_get(reflection.name)
|
290
|
+
association && Array.wrap(association.target).any?(&:changed_for_autosave?)
|
291
|
+
end
|
279
292
|
end
|
293
|
+
ensure
|
294
|
+
@_nested_records_changed_for_autosave_already_called = false
|
280
295
|
end
|
281
296
|
end
|
282
297
|
|
@@ -236,7 +236,7 @@ module ActiveRecord
|
|
236
236
|
@spec = spec
|
237
237
|
|
238
238
|
@checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
|
239
|
-
@reaper
|
239
|
+
@reaper = Reaper.new(self, (spec.config[:reaping_frequency] && spec.config[:reaping_frequency].to_f))
|
240
240
|
@reaper.run
|
241
241
|
|
242
242
|
# default max pool size to 5
|
@@ -365,7 +365,7 @@ module ActiveRecord
|
|
365
365
|
conn.expire
|
366
366
|
end
|
367
367
|
|
368
|
-
release owner
|
368
|
+
release conn, owner
|
369
369
|
|
370
370
|
@available.add conn
|
371
371
|
end
|
@@ -378,7 +378,7 @@ module ActiveRecord
|
|
378
378
|
@connections.delete conn
|
379
379
|
@available.delete conn
|
380
380
|
|
381
|
-
release conn.owner
|
381
|
+
release conn, conn.owner
|
382
382
|
|
383
383
|
@available.add checkout_new_connection if @available.any_waiting?
|
384
384
|
end
|
@@ -426,10 +426,12 @@ module ActiveRecord
|
|
426
426
|
end
|
427
427
|
end
|
428
428
|
|
429
|
-
def release(owner)
|
429
|
+
def release(conn, owner)
|
430
430
|
thread_id = owner.object_id
|
431
431
|
|
432
|
-
@reserved_connections
|
432
|
+
if @reserved_connections[thread_id] == conn
|
433
|
+
@reserved_connections.delete thread_id
|
434
|
+
end
|
433
435
|
end
|
434
436
|
|
435
437
|
def new_connection
|
@@ -258,7 +258,18 @@ module ActiveRecord
|
|
258
258
|
|
259
259
|
# Rolls back the transaction (and turns on auto-committing). Must be
|
260
260
|
# done if the transaction block raises an exception or returns false.
|
261
|
-
def rollback_db_transaction
|
261
|
+
def rollback_db_transaction
|
262
|
+
exec_rollback_db_transaction
|
263
|
+
end
|
264
|
+
|
265
|
+
def exec_rollback_db_transaction() end #:nodoc:
|
266
|
+
|
267
|
+
def rollback_to_savepoint(name = nil)
|
268
|
+
exec_rollback_to_savepoint(name)
|
269
|
+
end
|
270
|
+
|
271
|
+
def exec_rollback_to_savepoint(name = nil) #:nodoc:
|
272
|
+
end
|
262
273
|
|
263
274
|
def default_sequence_name(table, column)
|
264
275
|
nil
|
@@ -3,7 +3,7 @@ module ActiveRecord
|
|
3
3
|
module QueryCache
|
4
4
|
class << self
|
5
5
|
def included(base) #:nodoc:
|
6
|
-
dirties_query_cache base, :insert, :update, :delete
|
6
|
+
dirties_query_cache base, :insert, :update, :delete, :rollback_to_savepoint, :rollback_db_transaction
|
7
7
|
end
|
8
8
|
|
9
9
|
def dirties_query_cache(base, *method_names)
|
@@ -94,11 +94,12 @@ module ActiveRecord
|
|
94
94
|
# An array of ColumnDefinition objects, representing the column changes
|
95
95
|
# that have been defined.
|
96
96
|
attr_accessor :indexes
|
97
|
-
attr_reader :name, :temporary, :options, :as
|
97
|
+
attr_reader :name, :temporary, :options, :as, :foreign_keys
|
98
98
|
|
99
99
|
def initialize(types, name, temporary, options, as = nil)
|
100
100
|
@columns_hash = {}
|
101
101
|
@indexes = {}
|
102
|
+
@foreign_keys = {}
|
102
103
|
@native = types
|
103
104
|
@temporary = temporary
|
104
105
|
@options = options
|
@@ -286,6 +287,10 @@ module ActiveRecord
|
|
286
287
|
indexes[column_name] = options
|
287
288
|
end
|
288
289
|
|
290
|
+
def foreign_key(table_name, options = {}) # :nodoc:
|
291
|
+
foreign_keys[table_name] = options
|
292
|
+
end
|
293
|
+
|
289
294
|
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
|
290
295
|
# <tt>:updated_at</tt> to the table. See SchemaStatements#add_timestamps
|
291
296
|
#
|
@@ -297,9 +302,12 @@ module ActiveRecord
|
|
297
302
|
column(:updated_at, :datetime, options)
|
298
303
|
end
|
299
304
|
|
300
|
-
# Adds a reference. Optionally adds a +type+ column, if
|
301
|
-
# <tt
|
302
|
-
#
|
305
|
+
# Adds a reference. Optionally adds a +type+ column, if
|
306
|
+
# <tt>:polymorphic</tt> option is provided. <tt>references</tt> and
|
307
|
+
# <tt>belongs_to</tt> are acceptable. The reference column will be an
|
308
|
+
# +integer+ by default, the <tt>:type</tt> option can be used to specify
|
309
|
+
# a different type. A foreign key will be created if a +foreign_key+
|
310
|
+
# option is passed.
|
303
311
|
#
|
304
312
|
# t.references(:user)
|
305
313
|
# t.references(:user, type: "string")
|
@@ -310,11 +318,18 @@ module ActiveRecord
|
|
310
318
|
options = args.extract_options!
|
311
319
|
polymorphic = options.delete(:polymorphic)
|
312
320
|
index_options = options.delete(:index)
|
321
|
+
foreign_key_options = options.delete(:foreign_key)
|
313
322
|
type = options.delete(:type) || :integer
|
323
|
+
|
324
|
+
if polymorphic && foreign_key_options
|
325
|
+
raise ArgumentError, "Cannot add a foreign key on a polymorphic relation"
|
326
|
+
end
|
327
|
+
|
314
328
|
args.each do |col|
|
315
329
|
column("#{col}_id", type, options)
|
316
330
|
column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
|
317
331
|
index(polymorphic ? %w(type id).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
|
332
|
+
foreign_key(col.to_s.pluralize, foreign_key_options.is_a?(Hash) ? foreign_key_options : {}) if foreign_key_options
|
318
333
|
end
|
319
334
|
end
|
320
335
|
alias :belongs_to :references
|
@@ -520,9 +535,12 @@ module ActiveRecord
|
|
520
535
|
@base.rename_column(name, column_name, new_column_name)
|
521
536
|
end
|
522
537
|
|
523
|
-
# Adds a reference. Optionally adds a +type+ column, if
|
524
|
-
# <tt
|
525
|
-
#
|
538
|
+
# Adds a reference. Optionally adds a +type+ column, if
|
539
|
+
# <tt>:polymorphic</tt> option is provided. <tt>references</tt> and
|
540
|
+
# <tt>belongs_to</tt> are acceptable. The reference column will be an
|
541
|
+
# +integer+ by default, the <tt>:type</tt> option can be used to specify
|
542
|
+
# a different type. A foreign key will be created if a +foreign_key+
|
543
|
+
# option is passed.
|
526
544
|
#
|
527
545
|
# t.references(:user)
|
528
546
|
# t.references(:user, type: "string")
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'active_record/migration/join_table'
|
2
|
+
require 'active_support/core_ext/string/access'
|
3
|
+
require 'digest'
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters # :nodoc:
|
@@ -204,7 +206,17 @@ module ActiveRecord
|
|
204
206
|
end
|
205
207
|
|
206
208
|
result = execute schema_creation.accept td
|
207
|
-
|
209
|
+
|
210
|
+
unless supports_indexes_in_create?
|
211
|
+
td.indexes.each_pair do |column_name, index_options|
|
212
|
+
add_index(table_name, column_name, index_options)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
td.foreign_keys.each_pair do |other_table_name, foreign_key_options|
|
217
|
+
add_foreign_key(table_name, other_table_name, foreign_key_options)
|
218
|
+
end
|
219
|
+
|
208
220
|
result
|
209
221
|
end
|
210
222
|
|
@@ -576,9 +588,8 @@ module ActiveRecord
|
|
576
588
|
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
|
577
589
|
#
|
578
590
|
def rename_index(table_name, old_name, new_name)
|
579
|
-
|
580
|
-
|
581
|
-
end
|
591
|
+
validate_index_length!(table_name, new_name)
|
592
|
+
|
582
593
|
# this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
|
583
594
|
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
|
584
595
|
return unless old_index_def
|
@@ -627,7 +638,7 @@ module ActiveRecord
|
|
627
638
|
#
|
628
639
|
# add_belongs_to(:products, :supplier, polymorphic: true)
|
629
640
|
#
|
630
|
-
# ====== Create
|
641
|
+
# ====== Create supplier_id, supplier_type columns and appropriate index
|
631
642
|
#
|
632
643
|
# add_reference(:products, :supplier, polymorphic: true, index: true)
|
633
644
|
#
|
@@ -635,9 +646,16 @@ module ActiveRecord
|
|
635
646
|
polymorphic = options.delete(:polymorphic)
|
636
647
|
index_options = options.delete(:index)
|
637
648
|
type = options.delete(:type) || :integer
|
649
|
+
foreign_key_options = options.delete(:foreign_key)
|
650
|
+
|
651
|
+
if polymorphic && foreign_key_options
|
652
|
+
raise ArgumentError, "Cannot add a foreign key to a polymorphic relation"
|
653
|
+
end
|
654
|
+
|
638
655
|
add_column(table_name, "#{ref_name}_id", type, options)
|
639
656
|
add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
|
640
657
|
add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
|
658
|
+
add_foreign_key(table_name, ref_name.to_s.pluralize, foreign_key_options.is_a?(Hash) ? foreign_key_options : {}) if foreign_key_options
|
641
659
|
end
|
642
660
|
alias :add_belongs_to :add_reference
|
643
661
|
|
@@ -652,7 +670,13 @@ module ActiveRecord
|
|
652
670
|
#
|
653
671
|
# remove_reference(:products, :supplier, polymorphic: true)
|
654
672
|
#
|
673
|
+
# ====== Remove the reference with a foreign key
|
674
|
+
#
|
675
|
+
# remove_reference(:products, :user, index: true, foreign_key: true)
|
676
|
+
#
|
655
677
|
def remove_reference(table_name, ref_name, options = {})
|
678
|
+
remove_foreign_key table_name, ref_name.to_s.pluralize if options[:foreign_key]
|
679
|
+
|
656
680
|
remove_column(table_name, "#{ref_name}_id")
|
657
681
|
remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
|
658
682
|
end
|
@@ -982,8 +1006,16 @@ module ActiveRecord
|
|
982
1006
|
end
|
983
1007
|
|
984
1008
|
def foreign_key_name(table_name, options) # :nodoc:
|
1009
|
+
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
|
1010
|
+
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
985
1011
|
options.fetch(:name) do
|
986
|
-
"fk_rails_#{
|
1012
|
+
"fk_rails_#{hashed_identifier}"
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
def validate_index_length!(table_name, new_name)
|
1017
|
+
if new_name.length > allowed_index_name_length
|
1018
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
|
987
1019
|
end
|
988
1020
|
end
|
989
1021
|
end
|