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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +324 -0
  3. data/lib/active_record/associations/belongs_to_association.rb +4 -4
  4. data/lib/active_record/associations/collection_association.rb +4 -4
  5. data/lib/active_record/associations/has_many_association.rb +2 -2
  6. data/lib/active_record/associations/has_one_association.rb +2 -2
  7. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  8. data/lib/active_record/associations/join_dependency.rb +10 -12
  9. data/lib/active_record/associations.rb +6 -0
  10. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  11. data/lib/active_record/attribute_methods/read.rb +3 -3
  12. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  13. data/lib/active_record/attribute_methods/write.rb +3 -3
  14. data/lib/active_record/attribute_methods.rb +46 -6
  15. data/lib/active_record/autosave_association.rb +5 -2
  16. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -1
  17. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  18. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  19. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -4
  20. data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -15
  21. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +37 -13
  22. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +2 -1
  23. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -10
  24. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  25. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +3 -1
  26. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -2
  27. data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -20
  28. data/lib/active_record/core.rb +8 -3
  29. data/lib/active_record/counter_cache.rb +7 -3
  30. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  31. data/lib/active_record/database_configurations/hash_config.rb +6 -2
  32. data/lib/active_record/delegated_type.rb +6 -6
  33. data/lib/active_record/destroy_association_async_job.rb +1 -1
  34. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  35. data/lib/active_record/encryption/scheme.rb +8 -4
  36. data/lib/active_record/encryption.rb +2 -0
  37. data/lib/active_record/enum.rb +1 -1
  38. data/lib/active_record/future_result.rb +9 -0
  39. data/lib/active_record/gem_version.rb +2 -2
  40. data/lib/active_record/locking/optimistic.rb +1 -1
  41. data/lib/active_record/marshalling.rb +4 -1
  42. data/lib/active_record/message_pack.rb +1 -1
  43. data/lib/active_record/migration/compatibility.rb +6 -0
  44. data/lib/active_record/model_schema.rb +7 -2
  45. data/lib/active_record/nested_attributes.rb +13 -2
  46. data/lib/active_record/persistence.rb +2 -2
  47. data/lib/active_record/query_cache.rb +1 -1
  48. data/lib/active_record/query_logs_formatter.rb +1 -1
  49. data/lib/active_record/railtie.rb +14 -14
  50. data/lib/active_record/railties/databases.rake +2 -2
  51. data/lib/active_record/reflection.rb +8 -2
  52. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  53. data/lib/active_record/relation/query_methods.rb +21 -7
  54. data/lib/active_record/relation.rb +13 -3
  55. data/lib/active_record/result.rb +1 -1
  56. data/lib/active_record/tasks/database_tasks.rb +30 -8
  57. data/lib/active_record/test_fixtures.rb +1 -0
  58. data/lib/active_record/timestamp.rb +3 -1
  59. data/lib/active_record.rb +2 -2
  60. data/lib/arel/tree_manager.rb +5 -1
  61. data/lib/arel/visitors/to_sql.rb +2 -1
  62. 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
- connection.check_version
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)
@@ -629,7 +629,7 @@ module ActiveRecord
629
629
 
630
630
  result = internal_exec_query(sql, name, binds, prepare: prepare)
631
631
  if async
632
- FutureResult::Complete.new(result)
632
+ FutureResult.wrap(result)
633
633
  else
634
634
  result
635
635
  end
@@ -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::Complete.new(result)
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
- generate_index_name(table_name, options[:column])
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
- clear_cache!(new_connection: true)
719
- reset_transaction
720
- @raw_connection_dirty = false
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
- if @unconfigured_connection
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
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
- reconnect!(restore_transactions: true)
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
- sql = +"INSERT #{insert.into} #{insert.values_list}"
639
-
640
- if insert.skip_duplicates?
641
- no_op_column = quote_column_name(insert.keys.first)
642
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
643
- elsif insert.update_duplicates?
644
- sql << " ON DUPLICATE KEY UPDATE "
645
- if insert.raw_update_sql?
646
- sql << insert.raw_update_sql
647
- else
648
- sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
649
- sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
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
- !!@raw_connection&.ping
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
- super
143
- @raw_connection&.close
144
- @raw_connection = nil
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
- super
149
- @raw_connection&.automatic_close = false
150
- @raw_connection = nil
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
- @raw_connection&.close
166
- @raw_connection = nil
167
- connect
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", "~> 1.4"
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
- connection&.ping || false
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
- super
152
- unless connection.nil?
153
- connection.close
154
- self.connection = nil
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
- super
160
- unless connection.nil?
161
- connection.discard!
162
- self.connection = nil
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
- self.connection = self.class.new_client(@config)
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
- connection&.close
208
- self.connection = nil
203
+ @raw_connection&.close
204
+ @raw_connection = nil
209
205
  connect
210
206
  end
211
207
 
@@ -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 = nil
400
- @generated_association_methods = nil
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 = association.reflection.foreign_key.to_sym
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
@@ -46,7 +46,7 @@ module ActiveRecord
46
46
  attr_reader :uri
47
47
 
48
48
  def uri_parser
49
- @uri_parser ||= URI::Parser.new
49
+ @uri_parser ||= URI::RFC2396_Parser.new
50
50
  end
51
51
 
52
52
  # Converts the query parameters of the URI into a hash.
@@ -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
- "db/schema_cache.yml"
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, created_at, updated_at, entryable_type, entryable_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.to_sym => owner_id)
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
- @encryption_options ||= { key_provider: key_provider, cipher_options: { deterministic: deterministic? } }.compact
143
+ { key_provider: key_provider, cipher_options: { deterministic: deterministic? } }.compact
144
144
  end
145
145
 
146
146
  def decryption_options
147
- @decryption_options ||= { key_provider: key_provider }.compact
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
- @key_provider ||= @key_provider_param || build_key_provider || default_key_provider
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 build_key_provider
84
- return DerivedSecretKeyProvider.new(@key) if @key.present?
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
- if @deterministic
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
@@ -53,4 +53,6 @@ module ActiveRecord
53
53
  Cipher.eager_load!
54
54
  end
55
55
  end
56
+
57
+ ActiveSupport.run_load_hooks :active_record_encryption, Encryption
56
58
  end
@@ -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
 
@@ -9,8 +9,8 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 7
11
11
  MINOR = 1
12
- TINY = 3
13
- PRE = nil
12
+ TINY = 5
13
+ PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -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+ column and the locking facilities ensure that records instantiated twice
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