activerecord 7.1.3 → 7.1.5.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +324 -0
- data/lib/active_record/associations/belongs_to_association.rb +4 -4
- data/lib/active_record/associations/collection_association.rb +4 -4
- data/lib/active_record/associations/has_many_association.rb +2 -2
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +10 -12
- data/lib/active_record/associations.rb +6 -0
- data/lib/active_record/attribute_methods/dirty.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +3 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +46 -6
- data/lib/active_record/autosave_association.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -1
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -15
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +37 -13
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +2 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -10
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -2
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -20
- data/lib/active_record/core.rb +8 -3
- data/lib/active_record/counter_cache.rb +7 -3
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/database_configurations/hash_config.rb +6 -2
- data/lib/active_record/delegated_type.rb +6 -6
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/scheme.rb +8 -4
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +1 -1
- data/lib/active_record/future_result.rb +9 -0
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/marshalling.rb +4 -1
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/compatibility.rb +6 -0
- data/lib/active_record/model_schema.rb +7 -2
- data/lib/active_record/nested_attributes.rb +13 -2
- data/lib/active_record/persistence.rb +2 -2
- data/lib/active_record/query_cache.rb +1 -1
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/railtie.rb +14 -14
- data/lib/active_record/railties/databases.rake +2 -2
- data/lib/active_record/reflection.rb +8 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/query_methods.rb +21 -7
- data/lib/active_record/relation.rb +13 -3
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +30 -8
- data/lib/active_record/test_fixtures.rb +1 -0
- data/lib/active_record/timestamp.rb +3 -1
- data/lib/active_record.rb +2 -2
- data/lib/arel/tree_manager.rb +5 -1
- data/lib/arel/visitors/to_sql.rb +2 -1
- metadata +13 -13
@@ -675,7 +675,14 @@ module ActiveRecord
|
|
675
675
|
def new_connection
|
676
676
|
connection = Base.public_send(db_config.adapter_method, db_config.configuration_hash)
|
677
677
|
connection.pool = self
|
678
|
-
|
678
|
+
|
679
|
+
begin
|
680
|
+
connection.check_version
|
681
|
+
rescue
|
682
|
+
connection.disconnect!
|
683
|
+
raise
|
684
|
+
end
|
685
|
+
|
679
686
|
connection
|
680
687
|
rescue ConnectionNotEstablished => ex
|
681
688
|
raise ex.set_pool(self)
|
@@ -107,7 +107,7 @@ module ActiveRecord
|
|
107
107
|
|
108
108
|
if async
|
109
109
|
result = lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async)
|
110
|
-
FutureResult
|
110
|
+
FutureResult.wrap(result)
|
111
111
|
else
|
112
112
|
cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable, async: async) }
|
113
113
|
end
|
@@ -963,7 +963,11 @@ module ActiveRecord
|
|
963
963
|
def index_name(table_name, options) # :nodoc:
|
964
964
|
if Hash === options
|
965
965
|
if options[:column]
|
966
|
-
|
966
|
+
if options[:_uses_legacy_index_name]
|
967
|
+
"index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
|
968
|
+
else
|
969
|
+
generate_index_name(table_name, options[:column])
|
970
|
+
end
|
967
971
|
elsif options[:name]
|
968
972
|
options[:name]
|
969
973
|
else
|
@@ -1636,11 +1640,11 @@ module ActiveRecord
|
|
1636
1640
|
end
|
1637
1641
|
end
|
1638
1642
|
|
1639
|
-
def rename_table_indexes(table_name, new_name)
|
1643
|
+
def rename_table_indexes(table_name, new_name, **options)
|
1640
1644
|
indexes(new_name).each do |index|
|
1641
|
-
generated_index_name = index_name(table_name, column: index.columns)
|
1645
|
+
generated_index_name = index_name(table_name, column: index.columns, **options)
|
1642
1646
|
if generated_index_name == index.name
|
1643
|
-
rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
|
1647
|
+
rename_index new_name, generated_index_name, index_name(new_name, column: index.columns, **options)
|
1644
1648
|
end
|
1645
1649
|
end
|
1646
1650
|
end
|
@@ -711,13 +711,14 @@ module ActiveRecord
|
|
711
711
|
end
|
712
712
|
end
|
713
713
|
|
714
|
-
|
715
714
|
# Disconnects from the database if already connected. Otherwise, this
|
716
715
|
# method does nothing.
|
717
716
|
def disconnect!
|
718
|
-
|
719
|
-
|
720
|
-
|
717
|
+
@lock.synchronize do
|
718
|
+
clear_cache!(new_connection: true)
|
719
|
+
reset_transaction
|
720
|
+
@raw_connection_dirty = false
|
721
|
+
end
|
721
722
|
end
|
722
723
|
|
723
724
|
# Immediately forget this connection ever existed. Unlike disconnect!,
|
@@ -773,19 +774,17 @@ module ActiveRecord
|
|
773
774
|
# is no longer active, then this method will reconnect to the database.
|
774
775
|
def verify!
|
775
776
|
unless active?
|
776
|
-
|
777
|
-
@
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
return
|
784
|
-
end
|
777
|
+
@lock.synchronize do
|
778
|
+
if @unconfigured_connection
|
779
|
+
@raw_connection = @unconfigured_connection
|
780
|
+
@unconfigured_connection = nil
|
781
|
+
configure_connection
|
782
|
+
@verified = true
|
783
|
+
return
|
785
784
|
end
|
786
|
-
end
|
787
785
|
|
788
|
-
|
786
|
+
reconnect!(restore_transactions: true)
|
787
|
+
end
|
789
788
|
end
|
790
789
|
|
791
790
|
@verified = true
|
@@ -336,7 +336,7 @@ module ActiveRecord
|
|
336
336
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
337
337
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
338
338
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
339
|
-
rename_table_indexes(table_name, new_name)
|
339
|
+
rename_table_indexes(table_name, new_name, **options)
|
340
340
|
end
|
341
341
|
|
342
342
|
# Drops a table from the database.
|
@@ -635,18 +635,38 @@ module ActiveRecord
|
|
635
635
|
end
|
636
636
|
|
637
637
|
def build_insert_sql(insert) # :nodoc:
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
sql
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
638
|
+
no_op_column = quote_column_name(insert.keys.first)
|
639
|
+
|
640
|
+
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
641
|
+
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
642
|
+
if supports_insert_raw_alias_syntax?
|
643
|
+
values_alias = quote_table_name("#{insert.model.table_name}_values")
|
644
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
645
|
+
|
646
|
+
if insert.skip_duplicates?
|
647
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
|
648
|
+
elsif insert.update_duplicates?
|
649
|
+
if insert.raw_update_sql?
|
650
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
651
|
+
else
|
652
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
653
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
|
654
|
+
sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
|
655
|
+
end
|
656
|
+
end
|
657
|
+
else
|
658
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
659
|
+
|
660
|
+
if insert.skip_duplicates?
|
661
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
662
|
+
elsif insert.update_duplicates?
|
663
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
664
|
+
if insert.raw_update_sql?
|
665
|
+
sql << insert.raw_update_sql
|
666
|
+
else
|
667
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
668
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
669
|
+
end
|
650
670
|
end
|
651
671
|
end
|
652
672
|
|
@@ -853,6 +873,10 @@ module ActiveRecord
|
|
853
873
|
"DROP INDEX #{quote_column_name(index_name)}"
|
854
874
|
end
|
855
875
|
|
876
|
+
def supports_insert_raw_alias_syntax?
|
877
|
+
!mariadb? && database_version >= "8.0.19"
|
878
|
+
end
|
879
|
+
|
856
880
|
def supports_rename_index?
|
857
881
|
if mariadb?
|
858
882
|
database_version >= "10.5.2"
|
@@ -155,7 +155,7 @@ module ActiveRecord
|
|
155
155
|
end
|
156
156
|
|
157
157
|
def valid_primary_key_options
|
158
|
-
super + [:unsigned]
|
158
|
+
super + [:unsigned, :auto_increment]
|
159
159
|
end
|
160
160
|
|
161
161
|
def create_table_definition(name, **options)
|
@@ -185,6 +185,7 @@ module ActiveRecord
|
|
185
185
|
default, default_function = nil, default
|
186
186
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
187
187
|
default = +"(#{default})" unless default.start_with?("(")
|
188
|
+
default = default.gsub("\\'", "'")
|
188
189
|
default, default_function = nil, default
|
189
190
|
elsif type_metadata.type == :text && default&.start_with?("'")
|
190
191
|
# strip and unescape quotes
|
@@ -131,7 +131,7 @@ module ActiveRecord
|
|
131
131
|
#++
|
132
132
|
|
133
133
|
def active?
|
134
|
-
|
134
|
+
!(@raw_connection.nil? || @raw_connection.closed?) && @lock.synchronize { @raw_connection&.ping } || false
|
135
135
|
end
|
136
136
|
|
137
137
|
alias :reset! :reconnect!
|
@@ -139,15 +139,19 @@ module ActiveRecord
|
|
139
139
|
# Disconnects from the database if already connected.
|
140
140
|
# Otherwise, this method does nothing.
|
141
141
|
def disconnect!
|
142
|
-
|
143
|
-
|
144
|
-
|
142
|
+
@lock.synchronize do
|
143
|
+
super
|
144
|
+
@raw_connection&.close
|
145
|
+
@raw_connection = nil
|
146
|
+
end
|
145
147
|
end
|
146
148
|
|
147
149
|
def discard! # :nodoc:
|
148
|
-
|
149
|
-
|
150
|
-
|
150
|
+
@lock.synchronize do
|
151
|
+
super
|
152
|
+
@raw_connection&.automatic_close = false
|
153
|
+
@raw_connection = nil
|
154
|
+
end
|
151
155
|
end
|
152
156
|
|
153
157
|
private
|
@@ -162,9 +166,11 @@ module ActiveRecord
|
|
162
166
|
end
|
163
167
|
|
164
168
|
def reconnect
|
165
|
-
@
|
166
|
-
|
167
|
-
|
169
|
+
@lock.synchronize do
|
170
|
+
@raw_connection&.close
|
171
|
+
@raw_connection = nil
|
172
|
+
connect
|
173
|
+
end
|
168
174
|
end
|
169
175
|
|
170
176
|
def configure_connection
|
@@ -28,6 +28,12 @@ module ActiveRecord
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
# TODO: Remove when IPAddr#== compares IPAddr#prefix. See
|
32
|
+
# https://github.com/ruby/ipaddr/issues/21
|
33
|
+
def changed?(old_value, new_value, _new_value_before_type_cast)
|
34
|
+
!old_value.eql?(new_value) || !old_value.nil? && old_value.prefix != new_value.prefix
|
35
|
+
end
|
36
|
+
|
31
37
|
def cast_value(value)
|
32
38
|
if value.nil?
|
33
39
|
nil
|
@@ -247,6 +247,8 @@ module ActiveRecord
|
|
247
247
|
|
248
248
|
# Returns the sequence name for a table's primary key or some other specified key.
|
249
249
|
def default_sequence_name(table_name, pk = "id") # :nodoc:
|
250
|
+
return nil if pk.is_a?(Array)
|
251
|
+
|
250
252
|
result = serial_sequence(table_name, pk)
|
251
253
|
return nil unless result
|
252
254
|
Utils.extract_schema_qualified_name(result).to_s
|
@@ -400,7 +402,7 @@ module ActiveRecord
|
|
400
402
|
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
401
403
|
end
|
402
404
|
end
|
403
|
-
rename_table_indexes(table_name, new_name)
|
405
|
+
rename_table_indexes(table_name, new_name, **options)
|
404
406
|
end
|
405
407
|
|
406
408
|
def add_column(table_name, column_name, type, **options) # :nodoc:
|
@@ -11,7 +11,7 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
|
|
11
11
|
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
12
12
|
require "active_record/connection_adapters/sqlite3/schema_statements"
|
13
13
|
|
14
|
-
gem "sqlite3", "
|
14
|
+
gem "sqlite3", ">= 1.4"
|
15
15
|
require "sqlite3"
|
16
16
|
|
17
17
|
module ActiveRecord
|
@@ -286,7 +286,7 @@ module ActiveRecord
|
|
286
286
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
287
287
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
288
288
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
289
|
-
rename_table_indexes(table_name, new_name)
|
289
|
+
rename_table_indexes(table_name, new_name, **options)
|
290
290
|
end
|
291
291
|
|
292
292
|
def add_column(table_name, column_name, type, **options) # :nodoc:
|
@@ -102,6 +102,10 @@ module ActiveRecord
|
|
102
102
|
def initialize(...)
|
103
103
|
super
|
104
104
|
|
105
|
+
if @config[:prepared_statements]
|
106
|
+
raise ArgumentError, "Trilogy currently doesn't support prepared statements. Remove `prepared_statements: true` from your database configuration."
|
107
|
+
end
|
108
|
+
|
105
109
|
# Trilogy ignore `socket` if `host is set. We want the opposite to allow
|
106
110
|
# configuring UNIX domain sockets via `DATABASE_URL`.
|
107
111
|
@config.delete(:host) if @config[:socket]
|
@@ -140,7 +144,7 @@ module ActiveRecord
|
|
140
144
|
end
|
141
145
|
|
142
146
|
def active?
|
143
|
-
|
147
|
+
!(@raw_connection.nil? || @raw_connection.closed?) && @lock.synchronize { @raw_connection&.ping } || false
|
144
148
|
rescue ::Trilogy::Error
|
145
149
|
false
|
146
150
|
end
|
@@ -148,18 +152,18 @@ module ActiveRecord
|
|
148
152
|
alias reset! reconnect!
|
149
153
|
|
150
154
|
def disconnect!
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
+
@lock.synchronize do
|
156
|
+
super
|
157
|
+
@raw_connection&.close
|
158
|
+
@raw_connection = nil
|
155
159
|
end
|
156
160
|
end
|
157
161
|
|
158
162
|
def discard!
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
+
@lock.synchronize do
|
164
|
+
super
|
165
|
+
@raw_connection&.discard!
|
166
|
+
@raw_connection = nil
|
163
167
|
end
|
164
168
|
end
|
165
169
|
|
@@ -189,23 +193,15 @@ module ActiveRecord
|
|
189
193
|
exception.error_code if exception.respond_to?(:error_code)
|
190
194
|
end
|
191
195
|
|
192
|
-
def connection
|
193
|
-
@raw_connection
|
194
|
-
end
|
195
|
-
|
196
|
-
def connection=(conn)
|
197
|
-
@raw_connection = conn
|
198
|
-
end
|
199
|
-
|
200
196
|
def connect
|
201
|
-
|
197
|
+
@raw_connection = self.class.new_client(@config)
|
202
198
|
rescue ConnectionNotEstablished => ex
|
203
199
|
raise ex.set_pool(@pool)
|
204
200
|
end
|
205
201
|
|
206
202
|
def reconnect
|
207
|
-
|
208
|
-
|
203
|
+
@raw_connection&.close
|
204
|
+
@raw_connection = nil
|
209
205
|
connect
|
210
206
|
end
|
211
207
|
|
data/lib/active_record/core.rb
CHANGED
@@ -346,7 +346,7 @@ module ActiveRecord
|
|
346
346
|
|
347
347
|
# Returns a string like 'Post(id:integer, title:string, body:text)'
|
348
348
|
def inspect # :nodoc:
|
349
|
-
if self == Base
|
349
|
+
if self == Base || singleton_class?
|
350
350
|
super
|
351
351
|
elsif abstract_class?
|
352
352
|
"#{super}(abstract)"
|
@@ -396,8 +396,8 @@ module ActiveRecord
|
|
396
396
|
@arel_table = nil
|
397
397
|
@predicate_builder = nil
|
398
398
|
@inspection_filter = nil
|
399
|
-
@filter_attributes
|
400
|
-
@generated_association_methods
|
399
|
+
@filter_attributes ||= nil
|
400
|
+
@generated_association_methods ||= nil
|
401
401
|
end
|
402
402
|
end
|
403
403
|
|
@@ -701,6 +701,11 @@ module ActiveRecord
|
|
701
701
|
@strict_loading_mode == :n_plus_one_only
|
702
702
|
end
|
703
703
|
|
704
|
+
# Returns +true+ if the record uses strict_loading with +:all+ mode enabled.
|
705
|
+
def strict_loading_all?
|
706
|
+
@strict_loading_mode == :all
|
707
|
+
end
|
708
|
+
|
704
709
|
# Marks this record as read only.
|
705
710
|
#
|
706
711
|
# customer = Customer.first
|
@@ -65,7 +65,7 @@ module ActiveRecord
|
|
65
65
|
updates.merge!(touch_updates)
|
66
66
|
end
|
67
67
|
|
68
|
-
unscoped.where(primary_key => object.id).update_all(updates) if updates.any?
|
68
|
+
unscoped.where(primary_key => [object.id]).update_all(updates) if updates.any?
|
69
69
|
|
70
70
|
true
|
71
71
|
end
|
@@ -112,6 +112,7 @@ module ActiveRecord
|
|
112
112
|
# # `updated_at` = '2016-10-13T09:59:23-05:00'
|
113
113
|
# # WHERE id IN (10, 15)
|
114
114
|
def update_counters(id, counters)
|
115
|
+
id = [id] if composite_primary_key? && id.is_a?(Array) && !id[0].is_a?(Array)
|
115
116
|
unscoped.where!(primary_key => id).update_counters(counters)
|
116
117
|
end
|
117
118
|
|
@@ -198,8 +199,7 @@ module ActiveRecord
|
|
198
199
|
|
199
200
|
if affected_rows > 0
|
200
201
|
each_counter_cached_associations do |association|
|
201
|
-
foreign_key
|
202
|
-
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
|
202
|
+
unless destroyed_by_association && _foreign_keys_equal?(destroyed_by_association.foreign_key, association.reflection.foreign_key)
|
203
203
|
association.decrement_counters
|
204
204
|
end
|
205
205
|
end
|
@@ -213,5 +213,9 @@ module ActiveRecord
|
|
213
213
|
yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
|
214
214
|
end
|
215
215
|
end
|
216
|
+
|
217
|
+
def _foreign_keys_equal?(fkey1, fkey2)
|
218
|
+
fkey1 == fkey2 || Array(fkey1).map(&:to_sym) == Array(fkey2).map(&:to_sym)
|
219
|
+
end
|
216
220
|
end
|
217
221
|
end
|
@@ -114,8 +114,12 @@ module ActiveRecord
|
|
114
114
|
configuration_hash[:schema_cache_path]
|
115
115
|
end
|
116
116
|
|
117
|
-
def default_schema_cache_path
|
118
|
-
|
117
|
+
def default_schema_cache_path(db_dir = "db")
|
118
|
+
if primary?
|
119
|
+
File.join(db_dir, "schema_cache.yml")
|
120
|
+
else
|
121
|
+
File.join(db_dir, "#{name}_schema_cache.yml")
|
122
|
+
end
|
119
123
|
end
|
120
124
|
|
121
125
|
def lazy_schema_cache_path
|
@@ -36,7 +36,7 @@ module ActiveRecord
|
|
36
36
|
#
|
37
37
|
# Let's look at that entry/message/comment example using delegated types:
|
38
38
|
#
|
39
|
-
# # Schema: entries[ id, account_id, creator_id,
|
39
|
+
# # Schema: entries[ id, account_id, creator_id, entryable_type, entryable_id, created_at, updated_at ]
|
40
40
|
# class Entry < ApplicationRecord
|
41
41
|
# belongs_to :account
|
42
42
|
# belongs_to :creator
|
@@ -51,12 +51,12 @@ module ActiveRecord
|
|
51
51
|
# end
|
52
52
|
# end
|
53
53
|
#
|
54
|
-
# # Schema: messages[ id, subject, body ]
|
54
|
+
# # Schema: messages[ id, subject, body, created_at, updated_at ]
|
55
55
|
# class Message < ApplicationRecord
|
56
56
|
# include Entryable
|
57
57
|
# end
|
58
58
|
#
|
59
|
-
# # Schema: comments[ id, content ]
|
59
|
+
# # Schema: comments[ id, content, created_at, updated_at ]
|
60
60
|
# class Comment < ApplicationRecord
|
61
61
|
# include Entryable
|
62
62
|
# end
|
@@ -102,14 +102,14 @@ module ActiveRecord
|
|
102
102
|
# You create a new record that uses delegated typing by creating the delegator and delegatee at the same time,
|
103
103
|
# like so:
|
104
104
|
#
|
105
|
-
# Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user
|
105
|
+
# Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user, account: Current.account
|
106
106
|
#
|
107
107
|
# If you need more complicated composition, or you need to perform dependent validation, you should build a factory
|
108
108
|
# method or class to take care of the complicated needs. This could be as simple as:
|
109
109
|
#
|
110
110
|
# class Entry < ApplicationRecord
|
111
|
-
# def self.create_with_comment(content, creator: Current.user)
|
112
|
-
# create! entryable: Comment.new(content: content), creator: creator
|
111
|
+
# def self.create_with_comment(content, creator: Current.user, account: Current.account)
|
112
|
+
# create! entryable: Comment.new(content: content), creator: creator, account: account
|
113
113
|
# end
|
114
114
|
# end
|
115
115
|
#
|
@@ -19,7 +19,7 @@ module ActiveRecord
|
|
19
19
|
)
|
20
20
|
association_model = association_class.constantize
|
21
21
|
owner_class = owner_model_name.constantize
|
22
|
-
owner = owner_class.find_by(owner_class.primary_key
|
22
|
+
owner = owner_class.find_by(owner_class.primary_key => [owner_id])
|
23
23
|
|
24
24
|
if !owner_destroyed?(owner, ensuring_owner_was_method)
|
25
25
|
raise DestroyAssociationAsyncError, "owner record not destroyed"
|
@@ -140,11 +140,11 @@ module ActiveRecord
|
|
140
140
|
end
|
141
141
|
|
142
142
|
def encryption_options
|
143
|
-
|
143
|
+
{ key_provider: key_provider, cipher_options: { deterministic: deterministic? } }.compact
|
144
144
|
end
|
145
145
|
|
146
146
|
def decryption_options
|
147
|
-
|
147
|
+
{ key_provider: key_provider }.compact
|
148
148
|
end
|
149
149
|
|
150
150
|
def clean_text_scheme
|
@@ -50,7 +50,7 @@ module ActiveRecord
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def key_provider
|
53
|
-
@
|
53
|
+
@key_provider_param || key_provider_from_key || deterministic_key_provider || default_key_provider
|
54
54
|
end
|
55
55
|
|
56
56
|
def merge(other_scheme)
|
@@ -80,10 +80,14 @@ module ActiveRecord
|
|
80
80
|
raise Errors::Configuration, "key_provider: and key: can't be used simultaneously" if @key_provider_param && @key
|
81
81
|
end
|
82
82
|
|
83
|
-
def
|
84
|
-
|
83
|
+
def key_provider_from_key
|
84
|
+
@key_provider_from_key ||= if @key.present?
|
85
|
+
DerivedSecretKeyProvider.new(@key)
|
86
|
+
end
|
87
|
+
end
|
85
88
|
|
86
|
-
|
89
|
+
def deterministic_key_provider
|
90
|
+
@deterministic_key_provider ||= if @deterministic
|
87
91
|
DeterministicKeyProvider.new(ActiveRecord::Encryption.config.deterministic_key)
|
88
92
|
end
|
89
93
|
end
|
data/lib/active_record/enum.rb
CHANGED
@@ -247,7 +247,7 @@ module ActiveRecord
|
|
247
247
|
|
248
248
|
attribute(name, **options) do |subtype|
|
249
249
|
if subtype == ActiveModel::Type.default_value
|
250
|
-
raise "Undeclared attribute type for enum '#{name}'. Enums must be" \
|
250
|
+
raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
|
251
251
|
" backed by a database column or declared with an explicit type" \
|
252
252
|
" via `attribute`."
|
253
253
|
end
|
@@ -47,6 +47,15 @@ module ActiveRecord
|
|
47
47
|
|
48
48
|
Canceled = Class.new(ActiveRecordError)
|
49
49
|
|
50
|
+
def self.wrap(result)
|
51
|
+
case result
|
52
|
+
when self, Complete
|
53
|
+
result
|
54
|
+
else
|
55
|
+
Complete.new(result)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
50
59
|
delegate :empty?, :to_a, to: :result
|
51
60
|
delegate_missing_to :result
|
52
61
|
|
@@ -14,7 +14,7 @@ module ActiveRecord
|
|
14
14
|
# == Usage
|
15
15
|
#
|
16
16
|
# Active Record supports optimistic locking if the +lock_version+ field is present. Each update to the
|
17
|
-
# record increments the +lock_version+
|
17
|
+
# record increments the integer column +lock_version+ and the locking facilities ensure that records instantiated twice
|
18
18
|
# will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
|
19
19
|
#
|
20
20
|
# p1 = Person.find(1)
|
@@ -25,7 +25,10 @@ module ActiveRecord
|
|
25
25
|
payload = [attributes_for_database, new_record?]
|
26
26
|
|
27
27
|
cached_associations = self.class.reflect_on_all_associations.select do |reflection|
|
28
|
-
association_cached?(reflection.name)
|
28
|
+
if association_cached?(reflection.name)
|
29
|
+
association = association(reflection.name)
|
30
|
+
association.loaded? || association.target.present?
|
31
|
+
end
|
29
32
|
end
|
30
33
|
|
31
34
|
unless cached_associations.empty?
|
@@ -80,7 +80,7 @@ module ActiveRecord
|
|
80
80
|
|
81
81
|
def add_cached_associations(record, entry)
|
82
82
|
record.class.reflections.each_value do |reflection|
|
83
|
-
if record.association_cached?(reflection.name)
|
83
|
+
if record.association_cached?(reflection.name) && record.association(reflection.name).loaded?
|
84
84
|
entry << reflection.name << encode(record.association(reflection.name).target)
|
85
85
|
end
|
86
86
|
end
|