activerecord 7.2.2.1 → 7.2.3
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 -7
- data/README.rdoc +1 -1
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/belongs_to_association.rb +18 -2
- data/lib/active_record/associations/collection_association.rb +9 -7
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods.rb +24 -19
- data/lib/active_record/attributes.rb +37 -26
- data/lib/active_record/autosave_association.rb +22 -12
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +49 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -3
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -3
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -12
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +8 -3
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +10 -5
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/connection_handling.rb +12 -8
- data/lib/active_record/core.rb +28 -8
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/delegated_type.rb +18 -18
- data/lib/active_record/encryption/encryptable_record.rb +1 -1
- data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
- data/lib/active_record/encryption/encryptor.rb +21 -20
- data/lib/active_record/enum.rb +13 -12
- data/lib/active_record/errors.rb +3 -3
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/migration.rb +2 -1
- data/lib/active_record/query_logs.rb +4 -0
- data/lib/active_record/querying.rb +4 -4
- data/lib/active_record/railtie.rb +2 -2
- data/lib/active_record/railties/databases.rake +2 -1
- data/lib/active_record/relation/calculations.rb +35 -30
- data/lib/active_record/relation/finder_methods.rb +14 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +16 -9
- data/lib/active_record/relation/where_clause.rb +8 -2
- data/lib/active_record/relation.rb +15 -5
- data/lib/active_record/schema_dumper.rb +29 -11
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +7 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -0
- data/lib/active_record/transactions.rb +3 -1
- data/lib/active_record.rb +1 -1
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/crud.rb +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/select_manager.rb +6 -2
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -0
- data/lib/arel/visitors/to_sql.rb +3 -1
- metadata +10 -13
|
@@ -229,6 +229,8 @@ module ActiveRecord
|
|
|
229
229
|
end
|
|
230
230
|
|
|
231
231
|
def defined_for?(name: nil, column: nil, **options)
|
|
232
|
+
options = options.slice(*self.options.keys)
|
|
233
|
+
|
|
232
234
|
(name.nil? || self.name == name.to_s) &&
|
|
233
235
|
(column.nil? || Array(self.column) == Array(column).map(&:to_s)) &&
|
|
234
236
|
options.all? { |k, v| self.options[k].to_s == v.to_s }
|
|
@@ -302,8 +304,8 @@ module ActiveRecord
|
|
|
302
304
|
# t.exclusion_constraint("price WITH =, availability_range WITH &&", using: :gist, name: "price_check")
|
|
303
305
|
#
|
|
304
306
|
# See {connection.add_exclusion_constraint}[rdoc-ref:SchemaStatements#add_exclusion_constraint]
|
|
305
|
-
def exclusion_constraint(
|
|
306
|
-
@base.add_exclusion_constraint(name,
|
|
307
|
+
def exclusion_constraint(...)
|
|
308
|
+
@base.add_exclusion_constraint(name, ...)
|
|
307
309
|
end
|
|
308
310
|
|
|
309
311
|
# Removes the given exclusion constraint from the table.
|
|
@@ -311,8 +313,8 @@ module ActiveRecord
|
|
|
311
313
|
# t.remove_exclusion_constraint(name: "price_check")
|
|
312
314
|
#
|
|
313
315
|
# See {connection.remove_exclusion_constraint}[rdoc-ref:SchemaStatements#remove_exclusion_constraint]
|
|
314
|
-
def remove_exclusion_constraint(
|
|
315
|
-
@base.remove_exclusion_constraint(name,
|
|
316
|
+
def remove_exclusion_constraint(...)
|
|
317
|
+
@base.remove_exclusion_constraint(name, ...)
|
|
316
318
|
end
|
|
317
319
|
|
|
318
320
|
# Adds a unique constraint.
|
|
@@ -320,8 +322,8 @@ module ActiveRecord
|
|
|
320
322
|
# t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred)
|
|
321
323
|
#
|
|
322
324
|
# See {connection.add_unique_constraint}[rdoc-ref:SchemaStatements#add_unique_constraint]
|
|
323
|
-
def unique_constraint(
|
|
324
|
-
@base.add_unique_constraint(name,
|
|
325
|
+
def unique_constraint(...)
|
|
326
|
+
@base.add_unique_constraint(name, ...)
|
|
325
327
|
end
|
|
326
328
|
|
|
327
329
|
# Removes the given unique constraint from the table.
|
|
@@ -329,8 +331,8 @@ module ActiveRecord
|
|
|
329
331
|
# t.remove_unique_constraint(name: "unique_position")
|
|
330
332
|
#
|
|
331
333
|
# See {connection.remove_unique_constraint}[rdoc-ref:SchemaStatements#remove_unique_constraint]
|
|
332
|
-
def remove_unique_constraint(
|
|
333
|
-
@base.remove_unique_constraint(name,
|
|
334
|
+
def remove_unique_constraint(...)
|
|
335
|
+
@base.remove_unique_constraint(name, ...)
|
|
334
336
|
end
|
|
335
337
|
|
|
336
338
|
# Validates the given constraint on the table.
|
|
@@ -339,8 +341,8 @@ module ActiveRecord
|
|
|
339
341
|
# t.validate_constraint "price_check"
|
|
340
342
|
#
|
|
341
343
|
# See {connection.validate_constraint}[rdoc-ref:SchemaStatements#validate_constraint]
|
|
342
|
-
def validate_constraint(
|
|
343
|
-
@base.validate_constraint(name,
|
|
344
|
+
def validate_constraint(...)
|
|
345
|
+
@base.validate_constraint(name, ...)
|
|
344
346
|
end
|
|
345
347
|
|
|
346
348
|
# Validates the given check constraint on the table
|
|
@@ -349,8 +351,8 @@ module ActiveRecord
|
|
|
349
351
|
# t.validate_check_constraint name: "price_check"
|
|
350
352
|
#
|
|
351
353
|
# See {connection.validate_check_constraint}[rdoc-ref:SchemaStatements#validate_check_constraint]
|
|
352
|
-
def validate_check_constraint(
|
|
353
|
-
@base.validate_check_constraint(name,
|
|
354
|
+
def validate_check_constraint(...)
|
|
355
|
+
@base.validate_check_constraint(name, ...)
|
|
354
356
|
end
|
|
355
357
|
end
|
|
356
358
|
|
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
|
22
22
|
stream.puts " # Custom types defined in this database."
|
|
23
23
|
stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
|
|
24
24
|
types.sort.each do |name, values|
|
|
25
|
-
stream.puts " create_enum #{name.inspect}, #{values.
|
|
25
|
+
stream.puts " create_enum #{name.inspect}, #{values.inspect}"
|
|
26
26
|
end
|
|
27
27
|
stream.puts
|
|
28
28
|
end
|
|
@@ -879,7 +879,7 @@ module ActiveRecord
|
|
|
879
879
|
#
|
|
880
880
|
# validate_check_constraint :products, name: "price_check"
|
|
881
881
|
#
|
|
882
|
-
# The +options+ hash accepts the same keys as add_check_constraint[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
|
882
|
+
# The +options+ hash accepts the same keys as {add_check_constraint}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
|
883
883
|
def validate_check_constraint(table_name, **options)
|
|
884
884
|
chk_name_to_validate = check_constraint_for!(table_name, **options).name
|
|
885
885
|
|
|
@@ -25,7 +25,7 @@ module ActiveRecord
|
|
|
25
25
|
#
|
|
26
26
|
# The PostgreSQL adapter works with the native C (https://github.com/ged/ruby-pg) driver.
|
|
27
27
|
#
|
|
28
|
-
# Options
|
|
28
|
+
# ==== Options
|
|
29
29
|
#
|
|
30
30
|
# * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
|
|
31
31
|
# the default is to connect to localhost.
|
|
@@ -345,6 +345,7 @@ module ActiveRecord
|
|
|
345
345
|
@lock.synchronize do
|
|
346
346
|
return false unless @raw_connection
|
|
347
347
|
@raw_connection.query ";"
|
|
348
|
+
verified!
|
|
348
349
|
end
|
|
349
350
|
true
|
|
350
351
|
rescue PG::Error
|
|
@@ -503,7 +504,7 @@ module ActiveRecord
|
|
|
503
504
|
type.typname AS name,
|
|
504
505
|
type.OID AS oid,
|
|
505
506
|
n.nspname AS schema,
|
|
506
|
-
|
|
507
|
+
array_agg(enum.enumlabel ORDER BY enum.enumsortorder) AS value
|
|
507
508
|
FROM pg_enum AS enum
|
|
508
509
|
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
|
|
509
510
|
JOIN pg_namespace n ON type.typnamespace = n.oid
|
|
@@ -612,7 +613,11 @@ module ActiveRecord
|
|
|
612
613
|
# Returns the version of the connected PostgreSQL server.
|
|
613
614
|
def get_database_version # :nodoc:
|
|
614
615
|
with_raw_connection do |conn|
|
|
615
|
-
conn.server_version
|
|
616
|
+
version = conn.server_version
|
|
617
|
+
if version == 0
|
|
618
|
+
raise ActiveRecord::ConnectionFailed, "Could not determine PostgreSQL version"
|
|
619
|
+
end
|
|
620
|
+
version
|
|
616
621
|
end
|
|
617
622
|
end
|
|
618
623
|
alias :postgresql_version :database_version
|
|
@@ -50,6 +50,19 @@ module ActiveRecord
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def quote(value) # :nodoc:
|
|
54
|
+
case value
|
|
55
|
+
when Numeric
|
|
56
|
+
if value.finite?
|
|
57
|
+
super
|
|
58
|
+
else
|
|
59
|
+
"'#{value}'"
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
53
66
|
def quote_string(s)
|
|
54
67
|
::SQLite3::Database.quote(s)
|
|
55
68
|
end
|
|
@@ -74,6 +74,7 @@ module ActiveRecord
|
|
|
74
74
|
Base.pluralize_table_names ? table.pluralize : table
|
|
75
75
|
end
|
|
76
76
|
table = strip_table_name_prefix_and_suffix(table)
|
|
77
|
+
options = options.slice(*fk.options.keys)
|
|
77
78
|
fk_to_table = strip_table_name_prefix_and_suffix(fk.to_table)
|
|
78
79
|
fk_to_table == table && options.all? { |k, v| fk.options[k].to_s == v.to_s }
|
|
79
80
|
end || raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
|
|
@@ -21,7 +21,7 @@ module ActiveRecord
|
|
|
21
21
|
# The SQLite3 adapter works with the sqlite3-ruby drivers
|
|
22
22
|
# (available as gem from https://rubygems.org/gems/sqlite3).
|
|
23
23
|
#
|
|
24
|
-
# Options
|
|
24
|
+
# ==== Options
|
|
25
25
|
#
|
|
26
26
|
# * <tt>:database</tt> - Path to the database file.
|
|
27
27
|
class SQLite3Adapter < AbstractAdapter
|
|
@@ -199,7 +199,12 @@ module ActiveRecord
|
|
|
199
199
|
!(@raw_connection.nil? || @raw_connection.closed?)
|
|
200
200
|
end
|
|
201
201
|
|
|
202
|
-
|
|
202
|
+
def active?
|
|
203
|
+
if connected?
|
|
204
|
+
verified!
|
|
205
|
+
true
|
|
206
|
+
end
|
|
207
|
+
end
|
|
203
208
|
|
|
204
209
|
alias :reset! :reconnect!
|
|
205
210
|
|
|
@@ -367,7 +372,7 @@ module ActiveRecord
|
|
|
367
372
|
end
|
|
368
373
|
alias :add_belongs_to :add_reference
|
|
369
374
|
|
|
370
|
-
FK_REGEX = /.*FOREIGN KEY\s+\("(
|
|
375
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("([^"]+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
|
371
376
|
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
|
372
377
|
def foreign_keys(table_name)
|
|
373
378
|
# SQLite returns 1 row for each column of composite foreign keys.
|
|
@@ -542,8 +547,8 @@ module ActiveRecord
|
|
|
542
547
|
yield definition if block_given?
|
|
543
548
|
end
|
|
544
549
|
|
|
545
|
-
|
|
546
|
-
|
|
550
|
+
disable_referential_integrity do
|
|
551
|
+
transaction do
|
|
547
552
|
move_table(table_name, altered_table_name, options.merge(temporary: true))
|
|
548
553
|
move_table(altered_table_name, table_name, &caller)
|
|
549
554
|
end
|
|
@@ -170,9 +170,11 @@ module ActiveRecord
|
|
|
170
170
|
prevent_writes = true if role == ActiveRecord.reading_role
|
|
171
171
|
|
|
172
172
|
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
begin
|
|
174
|
+
yield
|
|
175
|
+
ensure
|
|
176
|
+
connected_to_stack.pop
|
|
177
|
+
end
|
|
176
178
|
end
|
|
177
179
|
|
|
178
180
|
# Use a specified connection.
|
|
@@ -373,11 +375,13 @@ module ActiveRecord
|
|
|
373
375
|
prevent_writes = true if role == ActiveRecord.reading_role
|
|
374
376
|
|
|
375
377
|
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
378
|
+
begin
|
|
379
|
+
return_value = yield
|
|
380
|
+
return_value.load if return_value.is_a? ActiveRecord::Relation
|
|
381
|
+
return_value
|
|
382
|
+
ensure
|
|
383
|
+
self.connected_to_stack.pop
|
|
384
|
+
end
|
|
381
385
|
end
|
|
382
386
|
|
|
383
387
|
def append_to_connected_to_stack(entry)
|
data/lib/active_record/core.rb
CHANGED
|
@@ -265,7 +265,7 @@ module ActiveRecord
|
|
|
265
265
|
return super if StatementCache.unsupported_value?(id)
|
|
266
266
|
|
|
267
267
|
cached_find_by([primary_key], [id]) ||
|
|
268
|
-
raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", name, primary_key, id))
|
|
268
|
+
raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id.inspect}", name, primary_key, id))
|
|
269
269
|
end
|
|
270
270
|
|
|
271
271
|
def find_by(*args) # :nodoc:
|
|
@@ -585,7 +585,7 @@ module ActiveRecord
|
|
|
585
585
|
#
|
|
586
586
|
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
|
587
587
|
# topic.slice(:title, :author_name)
|
|
588
|
-
# => { "title" => "Budget", "author_name" => "Jason" }
|
|
588
|
+
# # => { "title" => "Budget", "author_name" => "Jason" }
|
|
589
589
|
#
|
|
590
590
|
#--
|
|
591
591
|
# Implemented by ActiveModel::Access#slice.
|
|
@@ -599,7 +599,7 @@ module ActiveRecord
|
|
|
599
599
|
#
|
|
600
600
|
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
|
601
601
|
# topic.values_at(:title, :author_name)
|
|
602
|
-
# => ["Budget", "Jason"]
|
|
602
|
+
# # => ["Budget", "Jason"]
|
|
603
603
|
#
|
|
604
604
|
#--
|
|
605
605
|
# Implemented by ActiveModel::Access#values_at.
|
|
@@ -676,12 +676,14 @@ module ActiveRecord
|
|
|
676
676
|
# Sets the record to strict_loading mode. This will raise an error
|
|
677
677
|
# if the record tries to lazily load an association.
|
|
678
678
|
#
|
|
679
|
+
# NOTE: Strict loading is disabled during validation in order to let the record validate its association.
|
|
680
|
+
#
|
|
679
681
|
# user = User.first
|
|
680
682
|
# user.strict_loading! # => true
|
|
681
683
|
# user.address.city
|
|
682
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
684
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
683
685
|
# user.comments.to_a
|
|
684
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
686
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
685
687
|
#
|
|
686
688
|
# ==== Parameters
|
|
687
689
|
#
|
|
@@ -701,7 +703,7 @@ module ActiveRecord
|
|
|
701
703
|
# user.address.city # => "Tatooine"
|
|
702
704
|
# user.comments.to_a # => [#<Comment:0x00...]
|
|
703
705
|
# user.comments.first.ratings.to_a
|
|
704
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
706
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
705
707
|
def strict_loading!(value = true, mode: :all)
|
|
706
708
|
unless [:all, :n_plus_one_only].include?(mode)
|
|
707
709
|
raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
|
|
@@ -723,11 +725,29 @@ module ActiveRecord
|
|
|
723
725
|
@strict_loading_mode == :all
|
|
724
726
|
end
|
|
725
727
|
|
|
726
|
-
#
|
|
728
|
+
# Prevents records from being written to the database:
|
|
729
|
+
#
|
|
730
|
+
# customer = Customer.new
|
|
731
|
+
# customer.readonly!
|
|
732
|
+
# customer.save # raises ActiveRecord::ReadOnlyRecord
|
|
727
733
|
#
|
|
728
734
|
# customer = Customer.first
|
|
729
735
|
# customer.readonly!
|
|
730
|
-
# customer.
|
|
736
|
+
# customer.update(name: 'New Name') # raises ActiveRecord::ReadOnlyRecord
|
|
737
|
+
#
|
|
738
|
+
# Read-only records cannot be deleted from the database either:
|
|
739
|
+
#
|
|
740
|
+
# customer = Customer.first
|
|
741
|
+
# customer.readonly!
|
|
742
|
+
# customer.destroy # raises ActiveRecord::ReadOnlyRecord
|
|
743
|
+
#
|
|
744
|
+
# Please, note that the objects themselves are still mutable in memory:
|
|
745
|
+
#
|
|
746
|
+
# customer = Customer.new
|
|
747
|
+
# customer.readonly!
|
|
748
|
+
# customer.name = 'New Name' # OK
|
|
749
|
+
#
|
|
750
|
+
# but you won't be able to persist the changes.
|
|
731
751
|
def readonly!
|
|
732
752
|
@readonly = true
|
|
733
753
|
end
|
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
|
28
28
|
# # For the Post with id #1, reset the comments_count
|
|
29
29
|
# Post.reset_counters(1, :comments)
|
|
30
30
|
#
|
|
31
|
-
# # Like above, but also touch the
|
|
31
|
+
# # Like above, but also touch the updated_at and/or updated_on
|
|
32
32
|
# # attributes.
|
|
33
33
|
# Post.reset_counters(1, :comments, touch: true)
|
|
34
34
|
def reset_counters(id, *counters, touch: nil)
|
|
@@ -81,7 +81,9 @@ module ActiveRecord
|
|
|
81
81
|
|
|
82
82
|
def resolved_adapter
|
|
83
83
|
adapter = uri.scheme && @uri.scheme.tr("-", "_")
|
|
84
|
-
adapter
|
|
84
|
+
if adapter && ActiveRecord.protocol_adapters[adapter]
|
|
85
|
+
adapter = ActiveRecord.protocol_adapters[adapter]
|
|
86
|
+
end
|
|
85
87
|
adapter
|
|
86
88
|
end
|
|
87
89
|
|
|
@@ -181,16 +181,16 @@ module ActiveRecord
|
|
|
181
181
|
# delegated_type :entryable, types: %w[ Message Comment ], dependent: :destroy
|
|
182
182
|
# end
|
|
183
183
|
#
|
|
184
|
-
#
|
|
185
|
-
#
|
|
186
|
-
# Entry.messages
|
|
187
|
-
#
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
# Entry.comments
|
|
191
|
-
#
|
|
192
|
-
#
|
|
193
|
-
#
|
|
184
|
+
# @entry.entryable_class # => Message or Comment
|
|
185
|
+
# @entry.entryable_name # => "message" or "comment"
|
|
186
|
+
# Entry.messages # => Entry.where(entryable_type: "Message")
|
|
187
|
+
# @entry.message? # => true when entryable_type == "Message"
|
|
188
|
+
# @entry.message # => returns the message record, when entryable_type == "Message", otherwise nil
|
|
189
|
+
# @entry.message_id # => returns entryable_id, when entryable_type == "Message", otherwise nil
|
|
190
|
+
# Entry.comments # => Entry.where(entryable_type: "Comment")
|
|
191
|
+
# @entry.comment? # => true when entryable_type == "Comment"
|
|
192
|
+
# @entry.comment # => returns the comment record, when entryable_type == "Comment", otherwise nil
|
|
193
|
+
# @entry.comment_id # => returns entryable_id, when entryable_type == "Comment", otherwise nil
|
|
194
194
|
#
|
|
195
195
|
# You can also declare namespaced types:
|
|
196
196
|
#
|
|
@@ -199,25 +199,25 @@ module ActiveRecord
|
|
|
199
199
|
# end
|
|
200
200
|
#
|
|
201
201
|
# Entry.access_notice_messages
|
|
202
|
-
# entry.access_notice_message
|
|
203
|
-
# entry.access_notice_message?
|
|
202
|
+
# @entry.access_notice_message
|
|
203
|
+
# @entry.access_notice_message?
|
|
204
204
|
#
|
|
205
|
-
#
|
|
205
|
+
# ==== Options
|
|
206
206
|
#
|
|
207
207
|
# The +options+ are passed directly to the +belongs_to+ call, so this is where you declare +dependent+ etc.
|
|
208
208
|
# The following options can be included to specialize the behavior of the delegated type convenience methods.
|
|
209
209
|
#
|
|
210
|
-
# [
|
|
210
|
+
# [+:foreign_key+]
|
|
211
211
|
# Specify the foreign key used for the convenience methods. By default this is guessed to be the passed
|
|
212
212
|
# +role+ with an "_id" suffix. So a class that defines a
|
|
213
213
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_id" as
|
|
214
214
|
# the default <tt>:foreign_key</tt>.
|
|
215
|
-
# [
|
|
215
|
+
# [+:foreign_type+]
|
|
216
216
|
# Specify the column used to store the associated object's type. By default this is inferred to be the passed
|
|
217
217
|
# +role+ with a "_type" suffix. A class that defines a
|
|
218
218
|
# <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_type" as
|
|
219
219
|
# the default <tt>:foreign_type</tt>.
|
|
220
|
-
# [
|
|
220
|
+
# [+:primary_key+]
|
|
221
221
|
# Specify the method that returns the primary key of associated object used for the convenience methods.
|
|
222
222
|
# By default this is +id+.
|
|
223
223
|
#
|
|
@@ -226,8 +226,8 @@ module ActiveRecord
|
|
|
226
226
|
# delegated_type :entryable, types: %w[ Message Comment ], primary_key: :uuid, foreign_key: :entryable_uuid
|
|
227
227
|
# end
|
|
228
228
|
#
|
|
229
|
-
#
|
|
230
|
-
#
|
|
229
|
+
# @entry.message_uuid # => returns entryable_uuid, when entryable_type == "Message", otherwise nil
|
|
230
|
+
# @entry.comment_uuid # => returns entryable_uuid, when entryable_type == "Comment", otherwise nil
|
|
231
231
|
def delegated_type(role, types:, **options)
|
|
232
232
|
belongs_to role, options.delete(:scope), **options.merge(polymorphic: true)
|
|
233
233
|
define_delegated_type_methods role, types: types, options: options
|
|
@@ -16,7 +16,7 @@ module ActiveRecord
|
|
|
16
16
|
class_methods do
|
|
17
17
|
# Encrypts the +name+ attribute.
|
|
18
18
|
#
|
|
19
|
-
#
|
|
19
|
+
# ==== Options
|
|
20
20
|
#
|
|
21
21
|
# * <tt>:key_provider</tt> - A key provider to provide encryption and decryption keys. Defaults to
|
|
22
22
|
# +ActiveRecord::Encryption.key_provider+.
|
|
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
|
15
15
|
delegate :key_provider, :downcase?, :deterministic?, :previous_schemes, :with_context, :fixed?, to: :scheme
|
|
16
16
|
delegate :accessor, :type, to: :cast_type
|
|
17
17
|
|
|
18
|
-
#
|
|
18
|
+
# ==== Options
|
|
19
19
|
#
|
|
20
20
|
# * <tt>:scheme</tt> - A +Scheme+ with the encryption properties for this attribute.
|
|
21
21
|
# * <tt>:cast_type</tt> - A type that will be used to serialize (before encrypting) and deserialize
|
|
@@ -12,33 +12,34 @@ module ActiveRecord
|
|
|
12
12
|
# It interacts with a KeyProvider for getting the keys, and delegate to
|
|
13
13
|
# ActiveRecord::Encryption::Cipher the actual encryption algorithm.
|
|
14
14
|
class Encryptor
|
|
15
|
-
#
|
|
15
|
+
# ==== Options
|
|
16
16
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
17
|
+
# [+:compress+]
|
|
18
|
+
# Boolean indicating whether records should be compressed before
|
|
19
|
+
# encryption. Defaults to +true+.
|
|
19
20
|
def initialize(compress: true)
|
|
20
21
|
@compress = compress
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
# Encrypts +clean_text+ and returns the encrypted result
|
|
24
|
+
# Encrypts +clean_text+ and returns the encrypted result.
|
|
24
25
|
#
|
|
25
26
|
# Internally, it will:
|
|
26
27
|
#
|
|
27
|
-
# 1. Create a new ActiveRecord::Encryption::Message
|
|
28
|
-
# 2. Compress and encrypt +clean_text+ as the message payload
|
|
29
|
-
# 3. Serialize it with +ActiveRecord::Encryption.message_serializer+
|
|
30
|
-
# by default)
|
|
31
|
-
# 4. Encode the result with
|
|
28
|
+
# 1. Create a new ActiveRecord::Encryption::Message.
|
|
29
|
+
# 2. Compress and encrypt +clean_text+ as the message payload.
|
|
30
|
+
# 3. Serialize it with +ActiveRecord::Encryption.message_serializer+
|
|
31
|
+
# (+ActiveRecord::Encryption::SafeMarshal+ by default).
|
|
32
|
+
# 4. Encode the result with Base64.
|
|
32
33
|
#
|
|
33
|
-
#
|
|
34
|
+
# ==== Options
|
|
34
35
|
#
|
|
35
|
-
# [
|
|
36
|
+
# [+:key_provider+]
|
|
36
37
|
# Key provider to use for the encryption operation. It will default to
|
|
37
38
|
# +ActiveRecord::Encryption.key_provider+ when not provided.
|
|
38
39
|
#
|
|
39
|
-
# [
|
|
40
|
+
# [+:cipher_options+]
|
|
40
41
|
# Cipher-specific options that will be passed to the Cipher configured in
|
|
41
|
-
# +ActiveRecord::Encryption.cipher
|
|
42
|
+
# +ActiveRecord::Encryption.cipher+.
|
|
42
43
|
def encrypt(clear_text, key_provider: default_key_provider, cipher_options: {})
|
|
43
44
|
clear_text = force_encoding_if_needed(clear_text) if cipher_options[:deterministic]
|
|
44
45
|
|
|
@@ -46,17 +47,17 @@ module ActiveRecord
|
|
|
46
47
|
serialize_message build_encrypted_message(clear_text, key_provider: key_provider, cipher_options: cipher_options)
|
|
47
48
|
end
|
|
48
49
|
|
|
49
|
-
# Decrypts an +encrypted_text+ and returns the result as clean text
|
|
50
|
+
# Decrypts an +encrypted_text+ and returns the result as clean text.
|
|
50
51
|
#
|
|
51
|
-
#
|
|
52
|
+
# ==== Options
|
|
52
53
|
#
|
|
53
|
-
# [
|
|
54
|
+
# [+:key_provider+]
|
|
54
55
|
# Key provider to use for the encryption operation. It will default to
|
|
55
|
-
# +ActiveRecord::Encryption.key_provider+ when not provided
|
|
56
|
+
# +ActiveRecord::Encryption.key_provider+ when not provided.
|
|
56
57
|
#
|
|
57
|
-
# [
|
|
58
|
+
# [+:cipher_options+]
|
|
58
59
|
# Cipher-specific options that will be passed to the Cipher configured in
|
|
59
|
-
# +ActiveRecord::Encryption.cipher
|
|
60
|
+
# +ActiveRecord::Encryption.cipher+.
|
|
60
61
|
def decrypt(encrypted_text, key_provider: default_key_provider, cipher_options: {})
|
|
61
62
|
message = deserialize_message(encrypted_text)
|
|
62
63
|
keys = key_provider.decryption_keys(message)
|
|
@@ -66,7 +67,7 @@ module ActiveRecord
|
|
|
66
67
|
raise Errors::Decryption
|
|
67
68
|
end
|
|
68
69
|
|
|
69
|
-
# Returns whether the text is encrypted or not
|
|
70
|
+
# Returns whether the text is encrypted or not.
|
|
70
71
|
def encrypted?(text)
|
|
71
72
|
deserialize_message(text)
|
|
72
73
|
true
|
data/lib/active_record/enum.rb
CHANGED
|
@@ -119,7 +119,18 @@ module ActiveRecord
|
|
|
119
119
|
# enum :status, [ :active, :archived ], instance_methods: false
|
|
120
120
|
# end
|
|
121
121
|
#
|
|
122
|
-
#
|
|
122
|
+
# By default, an +ArgumentError+ will be raised when assigning an invalid value:
|
|
123
|
+
#
|
|
124
|
+
# class Conversation < ActiveRecord::Base
|
|
125
|
+
# enum :status, [ :active, :archived ]
|
|
126
|
+
# end
|
|
127
|
+
#
|
|
128
|
+
# conversation = Conversation.new
|
|
129
|
+
#
|
|
130
|
+
# conversation.status = :unknown # 'unknown' is not a valid status (ArgumentError)
|
|
131
|
+
#
|
|
132
|
+
# If, instead, you want the enum value to be validated before saving, use the
|
|
133
|
+
# +:validate+ option:
|
|
123
134
|
#
|
|
124
135
|
# class Conversation < ActiveRecord::Base
|
|
125
136
|
# enum :status, [ :active, :archived ], validate: true
|
|
@@ -136,7 +147,7 @@ module ActiveRecord
|
|
|
136
147
|
# conversation.status = :active
|
|
137
148
|
# conversation.valid? # => true
|
|
138
149
|
#
|
|
139
|
-
#
|
|
150
|
+
# You may also pass additional validation options:
|
|
140
151
|
#
|
|
141
152
|
# class Conversation < ActiveRecord::Base
|
|
142
153
|
# enum :status, [ :active, :archived ], validate: { allow_nil: true }
|
|
@@ -152,16 +163,6 @@ module ActiveRecord
|
|
|
152
163
|
#
|
|
153
164
|
# conversation.status = :active
|
|
154
165
|
# conversation.valid? # => true
|
|
155
|
-
#
|
|
156
|
-
# Otherwise +ArgumentError+ will raise:
|
|
157
|
-
#
|
|
158
|
-
# class Conversation < ActiveRecord::Base
|
|
159
|
-
# enum :status, [ :active, :archived ]
|
|
160
|
-
# end
|
|
161
|
-
#
|
|
162
|
-
# conversation = Conversation.new
|
|
163
|
-
#
|
|
164
|
-
# conversation.status = :unknown # 'unknown' is not a valid status (ArgumentError)
|
|
165
166
|
module Enum
|
|
166
167
|
def self.extended(base) # :nodoc:
|
|
167
168
|
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
data/lib/active_record/errors.rb
CHANGED
|
@@ -13,7 +13,7 @@ module ActiveRecord
|
|
|
13
13
|
|
|
14
14
|
# Raised when the single-table inheritance mechanism fails to locate the subclass
|
|
15
15
|
# (for example due to improper usage of column that
|
|
16
|
-
# {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema
|
|
16
|
+
# {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema.inheritance_column]
|
|
17
17
|
# points to).
|
|
18
18
|
class SubclassNotFound < ActiveRecordError
|
|
19
19
|
end
|
|
@@ -431,7 +431,7 @@ module ActiveRecord
|
|
|
431
431
|
UnknownAttributeError = ActiveModel::UnknownAttributeError
|
|
432
432
|
|
|
433
433
|
# Raised when an error occurred while doing a mass assignment to an attribute through the
|
|
434
|
-
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
|
|
434
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
|
|
435
435
|
# The exception has an +attribute+ property that is the name of the offending attribute.
|
|
436
436
|
class AttributeAssignmentError < ActiveRecordError
|
|
437
437
|
attr_reader :exception, :attribute
|
|
@@ -444,7 +444,7 @@ module ActiveRecord
|
|
|
444
444
|
end
|
|
445
445
|
|
|
446
446
|
# Raised when there are multiple errors while doing a mass assignment through the
|
|
447
|
-
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=]
|
|
447
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=]
|
|
448
448
|
# method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
|
|
449
449
|
# objects, each corresponding to the error while assigning to an attribute.
|
|
450
450
|
class MultiparameterAssignmentErrors < ActiveRecordError
|
|
@@ -193,8 +193,25 @@ module ActiveRecord
|
|
|
193
193
|
|
|
194
194
|
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
|
|
195
195
|
joins = targets.map do |target|
|
|
196
|
-
join = {
|
|
197
|
-
|
|
196
|
+
join = {}
|
|
197
|
+
|
|
198
|
+
if rhs_key.is_a?(Array)
|
|
199
|
+
composite_key = ActiveRecord::FixtureSet.composite_identify(target, rhs_key)
|
|
200
|
+
composite_key.each do |column, value|
|
|
201
|
+
join[column] = value
|
|
202
|
+
end
|
|
203
|
+
else
|
|
204
|
+
join[rhs_key] = ActiveRecord::FixtureSet.identify(target, column_type)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
if lhs_key.is_a?(Array)
|
|
208
|
+
lhs_key.zip(model_metadata.primary_key_name).each do |fkey, pkey|
|
|
209
|
+
join[fkey] = @row[pkey]
|
|
210
|
+
end
|
|
211
|
+
else
|
|
212
|
+
join[lhs_key] = @row[model_metadata.primary_key_name]
|
|
213
|
+
end
|
|
214
|
+
|
|
198
215
|
association.timestamp_column_names.each do |col|
|
|
199
216
|
join[col] = @now
|
|
200
217
|
end
|
|
@@ -1532,7 +1532,8 @@ module ActiveRecord
|
|
|
1532
1532
|
return if down? && !migrated.include?(migration.version.to_i)
|
|
1533
1533
|
return if up? && migrated.include?(migration.version.to_i)
|
|
1534
1534
|
|
|
1535
|
-
|
|
1535
|
+
message = up? ? "Migrating to" : "Reverting"
|
|
1536
|
+
Base.logger.info "#{message} #{migration.name} (#{migration.version})" if Base.logger
|
|
1536
1537
|
|
|
1537
1538
|
ddl_transaction(migration) do
|
|
1538
1539
|
migration.migrate(@direction)
|