rails 7.1.0 版本发布

Rails 是使用 Ruby 语言编写的 Web 应用开发框架,目的是通过解决快速开发中的共通问题,简化 Web 应用的开发。与其他编程语言和框架相比,使用 Rails 只需编写更少代码就能实现更多功能。有经验的 Rails 程序员常说,Rails 让 Web 应用开发变得更有趣。 以下是版本跟新内用以及bug 修复说明:

积极支持

  • 修复AS::MessagePackENV["RAILS_MAX_THREADS"].
  • 乔纳森·赫夫纳
  • 添加新的公共 API 用于广播日志
  • 这个功能已经存在了一段时间,但直到现在还是一个私有 API。
  • 广播日志允许将日志消息发送到不同的接收器(STDOUT、文件...),并且
  • 默认在开发环境中用于在 STDOUT 和
  • “development.log”文件中写入日志。
  • 基本用法:
stdout_logger = Logger.new(STDOUT)
file_logger = Logger.new("development.log")
broadcast = ActiveSupport::BroadcastLogger.new(stdout_logger, file_logger)
broadcast.info("Hello!") # The "Hello!" message is written on STDOUT and in the log file.
  • 将其他接收器添加到广播中:
broadcast = ActiveSupport::BroadcastLogger.new
broadcast.broadcast_to(Logger.new(STDERR))
  • 从广播中删除接收器:
stdout_logger = Logger.new(STDOUT)
broadcast = ActiveSupport::BroadcastLogger.new(stdout_logger)
broadcast.stop_broadcasting_to(stdout_logger)
  • 爱德华·钦
  • 修复范围#重叠?在 Ruby < 3.3 上不考虑空范围
  • 中田信义神谷翔一哈特利·麦奎尔
  • 使用 Ruby 3.3 Range#overlap?如果可供使用的话
  • 本田康夫
  • 添加bigdecimal为 Active Support 依赖项,该依赖项是 Ruby 3.4 的捆绑 gem 候选者。
  • bigdecimal将安装 3.1.4 或更高版本。
  • 如果 Ruby 2.7 和 3.0 用户想要将bigdecimal版本 2.0.0 或 3.0.0 行为作为默认 gem,
  • 请将该bigdecimal版本固定在应用程序 Gemfile 中。
  • 伊藤浩一
  • 添加drbmutex_mbase64是 Ruby 3.4 的捆绑 gem 候选者
  • 本田康夫
  • 当使用缓存格式版本 >= 7.1 或自定义序列化程序时,
  • 现在可以检测过期和版本不匹配的缓存条目,而无需反序列化
  • 其值。
  • 乔纳森·赫夫纳
  • 使所有缓存存储返回布尔值#delete
  • 以前,如果条目 存在则RedisCacheStore#delete返回,否则返回。现在,如果条目存在则返回 true, 否则返回 false,就像其他商店一样。1
  • 0
  • 如果该条目不存在,则会返回,现在FileStore也返回。nil
  • false
  • 佩特里克·德赫斯
  • Active Support 缓存存储现在支持通过选项替换默认压缩
  • :compressor。指定的压缩机必须响应deflate
  • inflate。例如:
module MyCompressor
  def self.deflate(string)
    # compression logic...
  end
  def self.inflate(compressed)
    # decompression logic...
  end
end
config.cache_store = :redis_cache_store, { compressor: MyCompressor }
  • 乔纳森·赫夫纳
  • Active Support 缓存存储现在支持一个:serializer选项。
  • 与选项类似:coder,序列化器必须响应dumpload。然而,
  • 序列化器只负责序列化缓存的值,而
  • 编码器则负责序列化整个ActiveSupport::Cache::Entry
  • 实例。此外,序列化器的输出可以自动
  • 压缩,而编码器则负责自己的压缩。
  • 指定序列化器而不是编码器还可以实现性能优化,包括缓存 格式版本 7.1
  • 引入的裸字符串优化。
  • 和选项是互斥的:serializer:coder指定
  • 两者都会引发一个ArgumentError.
  • 乔纳森·赫夫纳
  • 修复ActiveSupport::Inflector.humanize(nil)加注NoMethodError: undefined method `end_with?' for nil:NilClass
  • 詹姆斯·罗宾逊
  • 不要泄露秘密ActiveSupport::KeyGenerator#inspect
  • 前:
ActiveSupport::KeyGenerator.new(secret).inspect
"#<ActiveSupport::KeyGenerator:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"
  • 后:
ActiveSupport::KeyGenerator::Aes256Gcm(secret).inspect
"#<ActiveSupport::KeyGenerator:0x0000000104888038>"
  • 佩特里克·德赫斯
  • 改进在没有兼容版本的 Listen gem 的情况下使用 EventedFileUpdateChecker 时的错误消息
  • 哈特利·麦奎尔
  • 添加:report弃用行为
  • 设置config.active_support.deprecation = :report使用错误
  • 报告器向 报告弃用警告ActiveSupport::ErrorReporter
  • 弃用被报告为已处理错误,严重程度为:warning
  • 有助于向错误跟踪器报告生产中发生的弃用情况。
  • 艾蒂安·巴里
  • Range#overlaps?重命名#overlap?并添加别名以实现向后兼容性
  • 克里斯蒂安·施密特
  • 修复EncryptedConfiguration某些Hash
  • 方法返回错误值的问题
  • 哈特利·麦奎尔
  • 不要泄露秘密MessageEncryptor#inspect
  • 前:
ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm").inspect
"#<ActiveSupport::MessageEncryptor:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"
  • 后:
ActiveSupport::MessageEncryptor.new(secret, cipher: "aes-256-gcm").inspect
"#<ActiveSupport::MessageEncryptor:0x0000000104888038>"
  • 佩特里克·德赫斯
  • 不显示 的内容EncryptedConfiguration#inspect
  • 前:
Rails.application.credentials.inspect
"#<ActiveSupport::EncryptedConfiguration:0x000000010d2b38e8 ... @config={:secret=>\"something secret\"} ... @key_file_contents=\"915e4ea054e011022398dc242\" ...>"
  • 后:
Rails.application.credentials.inspect
"#<ActiveSupport::EncryptedConfiguration:0x000000010d2b38e8>"
  • 佩特里克·德赫斯
  • ERB::Util.html_escape_once总是返回一个html_safe字符串。
  • 此方法先前html_safe?在返回值上维护了字符串的属性
  • 。然而,由于该字符串已被转义,因此不将其标记为会html_safe导致
  • 实体被双重转义。
  • 举个例子,看看这个视图片段:
<p><%= html_escape_once("this & that &amp; the other") %></p>
  • 在此更改之前,这将被双重转义并呈现为:
<p>this &amp;amp; that &amp;amp; the other</p>
  • 进行此更改后,它正确呈现为:
<p>this &amp; that &amp; the other</p>
  • 修复#48256
  • 迈克·达莱西奥
  • 弃用SafeBuffer#clone_empty
  • 自 Rails 4.2.0 起,该方法已不再在内部使用。
  • 迈克·达莱西奥
  • MessageEncryptorMessageVerifierconfig.active_support.message_serializer
  • 现在接受:message_pack:message_pack_allow_marshal作为序列化器。
  • 这些序列化器需要msgpackgem
  • (>= 1.7.0)。
  • 消息包格式可以提供改进的性能和更小的有效负载
  • 大小。
  • 它还支持JSON不支持的一些 Ruby 类型的往返。例如:
verifier = ActiveSupport::MessageVerifier.new("secret")
data = [{ a: 1 }, { b: 2 }.with_indifferent_access, 1.to_d, Time.at(0, 123)]
message = verifier.generate(data)
# BEFORE with config.active_support.message_serializer = :json
verifier.verified(message)
# => [{"a"=>1}, {"b"=>2}, "1.0", "1969-12-31T18:00:00.000-06:00"]
verifier.verified(message).map(&:class)
# => [Hash, Hash, String, String]
# AFTER with config.active_support.message_serializer = :message_pack
verifier.verified(message)
# => [{:a=>1}, {"b"=>2}, 0.1e1, 1969-12-31 18:00:00.000123 -0600]
verifier.verified(message).map(&:class)
# => [Hash, ActiveSupport::HashWithIndifferentAccess, BigDecimal, Time]
  • 必要时,序列化程序可以回退到反序列化,并且序列:message_pack化 程序可以回退到使用 and 进行反序列化。此外,、和序列化器现在可以在必要时回退到反序列化。这些行为确保旧 消息仍然可以读取,从而使迁移更容易。
  • ActiveSupport::JSON:message_pack_allow_marshal
  • Marshal
  • ActiveSupport::JSON:marshal:json
  • :json_allow_marshal
  • ActiveSupport::MessagePack
  • 乔纳森·赫夫纳
  • 提供了一种新的缓存格式,其中包括对 裸字符串值(例如视图片段)7.1的优化。
  • 7.1新应用程序默认使用缓存格式,现有应用程序可以通过设置或 在文件中设置来
  • 启用该格式。config.load_defaults 7.1
  • config.active_support.cache_format_version = 7.1config/application.rb
  • config/environments/*.rb
  • 使用6.1或缓存格式写入的缓存条目可以 在使用该格式时7.0读取。要执行 Rails 7.1 升级的滚动部署 ,其中尚未升级的服务器必须能够 从已升级的服务器读取缓存,请在第一次部署时保持缓存格式不变 ,然后在后续部署中启用缓存格式。
  • 7.1
  • 7.1
  • 乔纳森·赫夫纳
  • ActiveSupport::MessagePackActive Support 缓存存储现在可以通过以下选项使用预配置的序列化器:serializer
config.cache_store = :redis_cache_store, { serializer: :message_pack }
  • 序列:message_pack化器可以减少缓存条目大小并提高
  • 性能,但需要msgpackgem
  • (>= 1.7.0)。
  • 序列:message_pack化器可以读取由默认
  • 序列化器写入的缓存条目,并且默认序列化器现在可以读取由
  • :message_pack序列化器写入的条目。这些行为使
  • 序列化器之间的迁移变得容易,而无需使整个缓存失效。
  • 乔纳森·赫夫纳
  • Object#deep_dup不再重复命名的类和模块。
  • 前:
hash = { class: Object, module: Kernel }
hash.deep_dup # => {:class=>#<Class:0x00000001063ffc80>, :module=>#<Module:0x00000001063ffa00>}
  • 后:
hash = { class: Object, module: Kernel }
hash.deep_dup # => {:class=>Object, :module=>Kernel}
  • 让·布西耶
  • ArgumentError如果该ActiveSupport::Cache键为空,则始终发出。
  • 约书亚·杨
  • 弃用 singleton 的使用ActiveSupport::Deprecation
  • 所有ActiveSupport::Deprecation作为单例的用法都已被弃用,最常见的一种是
  • ActiveSupport::Deprecation.warn. Gem 作者现在应该创建自己的弃用器(ActiveSupport::Deprecation
  • 对象),并使用它来发出弃用警告。
  • 不推荐调用以下任何函数而不指定 deprecator 参数:
  • 模块.弃用
  • 弃用常量
  • 已弃用的对象代理
  • 已弃用实例变量代理
  • 已弃用的ConstantProxy
  • 与弃用相关的测试断言
  • 诸如、 、之类的使用ActiveSupport::Deprecation.silence和配置方法现在应该针对应用程序的反对者behavior=disallowed_behavior=
  • disallowed_warnings=
Rails.application.deprecators.silence do
  # code that emits deprecation warnings
end
  • 如果你的 gem 有 Railtie 或 Engine,建议将你的 deprecator 添加到应用程序的 deprecator 中,这样与
  • deprecator 相关的配置选项也将应用到它,例如在生产环境中
  • config.active_support.report_deprecations设置为false也会禁用你的
  • deprecator。
initializer "my_gem.deprecator" do |app|
  app.deprecators[:my_gem] = MyGem.deprecator
end
  • 艾蒂安·巴里
  • 添加Object#with以设置和恢复块周围的公共属性
client.timeout # => 5
client.with(timeout: 1) do
  client.timeout # => 1
end
client.timeout # => 5
  • 让·布西耶
  • 删除已弃用的支持,以在提供不是上定义的常量之一的命名空间 ID 时生成不正确的 RFC 4122 UUID Digest::UUID
  • 拉斐尔·门东萨·弗朗萨
  • 弃用config.active_support.use_rfc4122_namespaced_uuids
  • 拉斐尔·门东萨·弗朗萨
  • 删除对象到Stringby的隐式转换ActiveSupport::SafeBuffer
  • 拉斐尔·门东萨·弗朗萨
  • 删除已弃用的active_support/core_ext/range/include_time_with_zone文件。
  • 拉斐尔·门东萨·弗朗萨
  • 弃用config.active_support.remove_deprecated_time_with_zone_name
  • 拉斐尔·门东萨·弗朗萨
  • 删除已弃用的ActiveSupport::TimeWithZone.name.
  • 拉斐尔·门东萨·弗朗萨
  • 弃用config.active_support.disable_to_s_conversion
  • 拉斐尔·门东萨·弗朗萨
  • 删除已弃用的将格式传递给#to_sin ArrayRangeDateDateTimeTime
  • BigDecimalFloat和的选项Integer
  • 拉斐尔·门东萨·弗朗萨
  • 删除已弃用的ActiveSupport::PerThreadRegistry.
  • 拉斐尔·门东萨·弗朗萨
  • 删除已弃用的Enumerable#sum.
  • 拉斐尔·门东萨·弗朗萨
  • ActiveSupport::Cache::MemCacheStore已弃用使用 的实例初始化 a Dalli::Client
  • Dalli::Client弃用提供已初始化的to实例的未记录选项ActiveSupport::Cache::MemCacheStore。此类客户端可能配置了无法识别的选项,这可能会导致意外行为。相反,请提供记录中的地址。
  • 阿勒杜斯特
  • 存根Time.new()_TimeHelpers#travel_to
travel_to Time.new(2004, 11, 24) do
  # Inside the `travel_to` block `Time.new` is stubbed
  assert_equal 2004, Time.new.year
end
  • 法特科迪马
  • 无论密码如何ActiveSupport::MessageEncryptor::InvalidMessage提高。 以前,当 a使用非 AEAD 密码(例如 AES-256-CBC)时,损坏或被篡改的消息会引发。现在,所有密码都会引发 相同的错误:
  • ActiveSupport::MessageEncryptor#decrypt_and_verify
  • MessageEncryptor
  • ActiveSupport::MessageVerifier::InvalidSignature
encryptor = ActiveSupport::MessageEncryptor.new("x" * 32, cipher: "aes-256-gcm")
message = encryptor.encrypt_and_sign("message")
encryptor.decrypt_and_verify(message.next)
# => raises ActiveSupport::MessageEncryptor::InvalidMessage
encryptor = ActiveSupport::MessageEncryptor.new("x" * 32, cipher: "aes-256-cbc")
message = encryptor.encrypt_and_sign("message")
encryptor.decrypt_and_verify(message.next)
# BEFORE:
# => raises ActiveSupport::MessageVerifier::InvalidSignature
# AFTER:
# => raises ActiveSupport::MessageEncryptor::InvalidMessage
  • 乔纳森·赫夫纳
  • nil使用时支持原始值ActiveSupport::MessageVerifier#verify
  • 以前,MessageVerifier#verify不能使用nil原始
  • 值,但 和MessageVerifier#verified
  • MessageEncryptor#decrypt_and_verify可以:
encryptor = ActiveSupport::MessageEncryptor.new(secret)
message = encryptor.encrypt_and_sign(nil)
encryptor.decrypt_and_verify(message)
# => nil
verifier = ActiveSupport::MessageVerifier.new(secret)
message = verifier.generate(nil)
verifier.verified(message)
# => nil
verifier.verify(message)
# BEFORE:
# => raises ActiveSupport::MessageVerifier::InvalidSignature
# AFTER:
# => nil
  • 乔纳森·赫夫纳
  • html_safe?当使用sliceslice!、 或chr方法进行切片时,保持html_safe 字符串。
  • 以前,仅当 使用方法html_safe?对 html_safe 字符串进行切片时才维护。现在,、、 和方法将保持类似的方法。
  • []sliceslice!chrhtml_safe?[]
string = "<div>test</div>".html_safe
string.slice(0, 1).html_safe? # => true
string.slice!(0, 1).html_safe? # => true
# maintain html_safe? after the slice!
string.html_safe? # => true
string.chr.html_safe? # => true
  • 迈克尔·戈
  • 添加Object#in?对开放范围的支持。
assert Date.today.in?(..Date.tomorrow)
assert_not Date.today.in?(Date.tomorrow..)
  • 伊格纳西奥·加林多
  • config.i18n.raise_on_missing_translations = true现在对任何缺失的翻译提出质疑。
  • 以前,它仅在视图或控制器中调用时才会引发。
  • 现在,只要I18n.t提供无法识别的密钥,它就会引发。
  • 如果您不希望出现此行为,您可以自定义 i18n 异常处理程序。
  • 有关详细信息,请参阅升级指南或 i18n 指南。
  • 亚历克斯·吉库列斯库
  • ActiveSupport::CurrentAttributes如果使用受限属性名称,现在会引发。
  • 不能使用set和 等属性,因为它们与公共 API 冲突。reset
  • CurrentAttributes
  • 亚历克斯·吉库列斯库
  • HashWithIndifferentAccess#transform_keys现在采用 Hash 参数,就像
  • RubyHash#transform_keys那样。
  • 松田晃
  • delegate现在在委托给类时定义具有适当数量的方法。
  • 通过此更改,它定义了更快的方法(没有参数时快 3.5 倍)。
  • 然而,为了获得这个好处,必须
  • 在声明委托之前定义委托目标方法。
# This defines 3.5 times faster method than before
class C
  def self.x() end
  delegate :x, to: :class
end
class C
  # This works but silently falls back to old behavior because
  # `delegate` cannot find the definition of `x`
  delegate :x, to: :class
  def self.x() end
end
  • 松田晃
  • assert_difference消息现在包含更改的内容。
  • 这使得调试不明显的故障变得更加容易。
  • 前:
"User.count" didn't change by 32.
Expected: 1611
  Actual: 1579
  • 后:
"User.count" didn't change by 32, but by 0.
Expected: 1611
  Actual: 1579
  • 亚历克斯·吉库列斯库
  • 添加将异常消息与assert_raises断言相匹配的功能
  • 而不是这个
error = assert_raises(ArgumentError) do
  perform_service(param: 'exception')
end
assert_match(/incorrect param/i, error.message)
  • 你现在可以写这个
assert_raises(ArgumentError, match: /incorrect param/i) do
  perform_service(param: 'exception')
end
  • 法特科迪马
  • 添加Rails.env.local?的简写Rails.env.development? || Rails.env.test?
  • DHH
  • ActiveSupport::Testing::TimeHelpers现在接受、和方法 的命名with_usec参数。传递 true 可以防止 用 截断目标时间。
  • freeze_timetraveltravel_to
  • change(usec: 0)
  • KevSlashNullserprex
  • ActiveSupport::CurrentAttributes.resets现在接受方法名称
  • 块 API 仍然是推荐的方法,但现在这两种 API 都受支持:
class Current < ActiveSupport::CurrentAttributes
  resets { Time.zone = nil }
  resets :clear_time_zone
end
  • 亚历克斯·吉库列斯库
  • 确保ActiveSupport::Testing::Isolation::Forking关闭管道
  • 之前,Forking.run_in_isolation打开了管道的两端。fork
  • 进程关闭读取端,对其进行写入,然后终止(这
  • 大概关闭了其末尾的文件描述符)。父进程
  • 关闭写端,从中读取,然后返回,从不关闭读
  • 端。
  • 这会导致打开的文件描述符累积,
  • 如果达到限制,可能会导致错误。
  • 萨姆·博斯托克
  • 修复夏令时结束前后的Time#change时间 。Time#advance
  • 以前,当Time#change或在 夏令时 (DST) 最后一段时间Time#advance内构建时间时,将 始终为当地时间选择非 DST 偏移量:
# DST ended just before 2021-11-07 2:00:00 AM in US/Eastern.
ENV["TZ"] = "US/Eastern"
time = Time.local(2021, 11, 07, 00, 59, 59) + 1
# => 2021-11-07 01:00:00 -0400
time.change(day: 07)
# => 2021-11-07 01:00:00 -0500
time.advance(seconds: 0)
# => 2021-11-07 01:00:00 -0500
time = Time.local(2021, 11, 06, 01, 00, 00)
# => 2021-11-06 01:00:00 -0400
time.change(day: 07)
# => 2021-11-07 01:00:00 -0500
time.advance(days: 1)
# => 2021-11-07 01:00:00 -0500
  • 并且始终会为有对象的时间选择 DST 偏移量TimeZone
Time.zone = "US/Eastern"<
>>
time = Time.new(2021, 11, 07, 02, 00, 00, Time.zone) - 3600
# => 2021-11-07 01:00:00 -0500
time.change(day: 07)
# => 2021-11-07 01:00:00 -0400
time.advance(seconds: 0)
# => 2021-11-07 01:00:00 -0400
time = Time.new(2021, 11, 8, 01, 00, 00, Time.zone)
# => 2021-11-08 01:00:00 -0500
time.change(day: 07)
# => 2021-11-07 01:00:00 -0400
time.advance(days: -1)
# => 2021-11-07 01:00:00 -0400
  • 现在,Time#change并且将 在可能的情况下选择与原始时间偏移Time#advance匹配的偏移:
ENV["TZ"] = "US/Eastern"<
>>
time = Time.local(2021, 11, 07, 00, 59, 59) + 1
# => 2021-11-07 01:00:00 -0400
time.change(day: 07)
# => 2021-11-07 01:00:00 -0400
time.advance(seconds: 0)
# => 2021-11-07 01:00:00 -0400
time = Time.local(2021, 11, 06, 01, 00, 00)
# => 2021-11-06 01:00:00 -0400
time.change(day: 07)
# => 2021-11-07 01:00:00 -0400
time.advance(days: 1)
# => 2021-11-07 01:00:00 -0400
Time.zone = "US/Eastern"
time = Time.new(2021, 11, 07, 02, 00, 00, Time.zone) - 3600
# => 2021-11-07 01:00:00 -0500
time.change(day: 07)
# => 2021-11-07 01:00:00 -0500
time.advance(seconds: 0)
# => 2021-11-07 01:00:00 -0500
time = Time.new(2021, 11, 8, 01, 00, 00, Time.zone)
# => 2021-11-08 01:00:00 -0500
time.change(day: 07)
# => 2021-11-07 01:00:00 -0500
time.advance(days: -1)
# => 2021-11-07 01:00:00 -0500
  • 凯文·霍尔西田隆吉乔纳森·海夫纳
  • 修复 MemoryStore 以在递增或递减时保留条目 TTL
  • 这是为了与 MemCachedStore 和 RedisCacheStore 的行为更加一致。
  • 让·布西耶
  • Rails.error.handleRails.error.record现在按多个错误类别进行过滤。
Rails.error.handle(IOError, ArgumentError) do
  1 + '1' # raises TypeError
end
1 + 1 # TypeErrors are not IOErrors or ArgumentError, so this will *not* be handled
  • 马丁·斯皮克曼
  • Class#subclasses现在Class#descendants自动过滤重新加载的类。
  • 以前,它们可以返回已
  • 取消引用但尚未垃圾收集的可重加载类的旧实现。
  • 他们现在会自动过滤此类,例如DescendantTracker#subclasses
  • DescendantTracker#descendants
  • 让·布西耶
  • Rails.error.report现在将错误标记为已报告以避免重复报告。
  • 在某些情况下,用户可能希望在错误
  • 出现之前使用一些额外的上下文来明确报告错误。
  • 这还允许安全地捕获和报告执行上下文之外的错误。
  • 让·布西耶
  • 添加assert_error_reportedassert_no_error_reported
  • 允许轻松断言发生了错误但已处理
report = assert_error_reported(IOError) do
  # ...
end
assert_equal "Oops", report.error.message
assert_equal "admin", report.context[:section]
assert_equal :warning, report.severity
assert_predicate report, :handled?
  • 让·布西耶
  • ActiveSupport::Deprecation行为回调现在可以接收
  • deprecator 实例作为参数。这使得此类回调可以更轻松地
  • 根据弃用者的状态更改其行为。例如,
  • 基于弃用者的debug标志。
  • 如下所示的 3-arity 和 splat-args 回调现在将传递
  • deprecator 实例作为其第三个参数:
  • ->(message, callstack, deprecator) { ... }
  • ->(*args) { ... }
  • ->(message, *other_args) { ... }
  • 如下所示的 2 元数和 4 元数回调的行为将继续
  • 与以前相同:
  • ->(message, callstack) { ... }
  • ->(message, callstack, deprecation_horizon, gem_name) { ... }
  • ->(message, callstack, *deprecation_details) { ... }
  • 乔纳森·赫夫纳
  • ActiveSupport::Deprecation#disallowed_warnings
  • 现在会影响配置它的实例。
  • 这意味着各个ActiveSupport::Deprecation实例可以
  • 配置自己的禁止警告,而全局
  • ActiveSupport::Deprecation.disallowed_warnings现在仅影响全局
  • ActiveSupport::Deprecation.warn.
ActiveSupport::Deprecation.disallowed_warnings = ["foo"]
deprecator = ActiveSupport::Deprecation.new("2.0", "MyCoolGem")
deprecator.disallowed_warnings = ["bar"]
ActiveSupport::Deprecation.warn("foo") # => raise ActiveSupport::DeprecationException
ActiveSupport::Deprecation.warn("bar") # => print "DEPRECATION WARNING: bar"
deprecator.warn("foo")                 # => raise ActiveSupport::DeprecationException
deprecator.warn("bar")                 # => print "DEPRECATION WARNING: bar"
ActiveSupport::Deprecation.disallowed_warnings = ["foo"]
deprecator = ActiveSupport::Deprecation.new("2.0", "MyCoolGem")
deprecator.disallowed_warnings = ["bar"]
ActiveSupport::Deprecation.warn("foo") # => raise ActiveSupport::DeprecationException
ActiveSupport::Deprecation.warn("bar") # => print "DEPRECATION WARNING: bar"
deprecator.warn("foo")                 # => print "DEPRECATION WARNING: foo"
deprecator.warn("bar")                 # => raise ActiveSupport::DeprecationException
  • ActiveSupport::Deprecation请注意,诸如ActiveSupport::Deprecation.warn
  • 和 之类的全局方法ActiveSupport::Deprecation.disallowed_warnings已被弃用。
  • 乔纳森·赫夫纳
  • 添加斜体和下划线支持ActiveSupport::LogSubscriber#color
  • 以前,通过位置参数仅支持粗体文本。 这允许为彩色日志
  • 指定粗体、斜体和下划线选项。
info color("Hello world!", :red, bold: true, underline: true)
  • 甘农·麦吉本
  • 添加String#downcase_first方法。
  • 该方法是 的推论String#upcase_first
  • 马克·施奈德
  • thread_mattr_accessor将调用.dup.freeze非冻结的默认值。
  • 这提供了基本级别的保护,防止不同线程尝试
  • 改变共享默认对象。
  • 乔纳森·赫夫纳
  • 添加raise_on_invalid_cache_expiration_time配置到ActiveSupport::Cache::Store
  • 指定如果给出了无效或时间则是否ArgumentError应引发。Rails.cache fetch
  • writeexpires_atexpires_in
  • 选项有true、 和false。如果false,则异常将被报告
  • handled并记录。默认为trueif config.load_defaults >= 7.1
  • 特雷弗·特克
  • ActiveSupport::Cache:Store#fetch现在将选项访问器传递给块。
  • 它可以覆盖缓存选项:
Rails.cache.fetch("3rd-party-token") do |name, options|
  token = fetch_token_from_remote
  # set cache's TTL to match token's TTL
  options.expires_in = token.expires_in
  token
end
  • 安德烈·格拉德基,让·布西耶
  • defaultnow的选项thread_mattr_accessor通过继承以及
  • 跨新线程应用。
  • 以前,default提供的值仅在定义属性编写器时设置,这将导致该属性在 后代和其他线程
  • 中未初始化。
  • 修复#43312
  • 蒂埃里·迪奥
  • Redis 缓存存储现在与 redis-rb 5.0 兼容。
  • 让·布西耶
  • 添加skip_nil:ActiveSupport::Cache::Store#fetch_multi.
  • 丹尼尔·阿尔法罗
  • 添加quarter日期/时间方法
  • 马特·斯旺森
  • 修复NoMethodError自定义ActiveSupport::Deprecation行为。
  • ActiveSupport::Deprecation.behavior=应该接受任何
  • 响应 的对象call,但实际上它的内部实现假设
  • 该对象可以响应arity,因此它仅限于Proc对象。
  • 此更改消除了arity对自定义行为的限制。
  • 中村亮
  • 支持:url_safe选项MessageEncryptor
  • 构造函数MessageEncryptor现在接受一个:url_safe选项,类似于
  • 构造MessageVerifier函数。启用后,此选项可确保
  • 消息使用 URL 安全编码。
  • 乔纳森·赫夫纳
  • 将选项添加url_safeActiveSupport::MessageVerifier初始值设定项
  • ActiveSupport::MessageVerifier.new现在采用可选url_safe参数。
  • 它可以通过传递 来生成 URL 安全的字符串url_safe: true
verifier = ActiveSupport::MessageVerifier.new(url_safe: true)
message = verifier.generate(data) # => URL-safe string
  • 该选项false默认是向后兼容的。
  • 神谷庄一
  • 默认为MemCacheStore和启用连接池RedisCacheStore
  • 如果要禁用连接池,请在配置缓存存储时将:pool选项设置为:false
config.cache_store = :mem_cache_store, "cache.example.com", pool: false
  • 法特科迪马
  • 添加force:ActiveSupport::Cache::Store#fetch_multi.
  • 法特科迪马
  • 已弃用:pool_size以及:pool_timeout用于在缓存存储中配置连接池的选项。
  • 用于使用pool: true默认设置启用池化:
config.cache_store = :redis_cache_store, pool: true
  • 或者通过选项传递单个选项:pool
config.cache_store = :redis_cache_store, pool: { size: 10, timeout: 2 }
  • 法特科迪马
  • 允许子类的#increment和#decrement方法ActiveSupport::Cache::Store
  • 设置新值。
  • 以前递增或递减未设置的键会失败并返回
  • nil。现在将采用默认值并创建密钥。
  • 安德烈·布拉戈耶维奇尤金·肯尼
  • 添加skip_nil:支持RedisCacheStore
  • 乔伊·帕里斯
  • ActiveSupport::Cache::MemoryStore#write(name, val, unless_exist:true)现在
  • 可以正确写入过期的密钥。
  • 艾伦·萨维奇
  • ActiveSupport::ErrorReporter现在接受并转发source:参数。
  • 这使得图书馆可以发出错误的来源信号,而报告者
  • 可以轻松忽略某些来源。
  • 让·布西耶
  • ActionView::Helpers修复和中的 XSS 并添加保护ERB::Util
  • 遵循XML规范,添加转义标签名称和属性名称中ERB::Util.xml_name_escape危险字符的方法。
  • 阿尔瓦罗·马丁·弗拉瓜斯
  • RespectActiveSupport::Logger.new:formatter关键字参数
  • stdlibLogger::new允许传递:formatter关键字参数来
  • 设置记录器的格式化程序。以前 通过始终将格式化程序设置为 的实例来ActiveSupport::Logger.new忽略该参数。
  • ActiveSupport::Logger::SimpleFormatter
  • 史蒂文·哈曼
  • 弃用保留 Ruby 2.4 之前的行为to_time
  • 在 Ruby 2.4+ 中,+to_time+ 的默认值从转换为
  • 本地系统时间更改为保留接收器的偏移量。当时 Rails
  • 支持旧版本的 Ruby,因此添加了一个兼容层来协助
  • 迁移过程。从 Rails 5.0 开始,新应用程序默认采用
  • Ruby 2.4+ 行为,并且由于 Rails 7.0 现在仅支持 Ruby 2.7+,因此
  • 可以安全地删除此兼容性层。
  • 为了最大限度地减少生成的任何噪音,仅当设置配置为时才会出现弃用警告,因为这是 删除兼容层会产生任何影响的false唯一情况。
  • 安德鲁·怀特
  • Pathname.blank?只返回 truePathname.new("")
  • 以前,如果路径存在并且是空目录或文件,它最终会调用Pathname#empty?返回 true 。
  • 这种行为不太可能发生。
  • 让·布西耶
  • 弃用Notification::Event#children_#parent_of?
  • 约翰·霍索恩
  • 使用时将默认序列化器ActiveSupport::MessageVerifier从 from
  • Marshal更改为.ActiveSupport::JSONconfig.load_defaults 7.1
  • Marshal仍然可以读取序列化的消息,但新消息将
  • 使用 序列化ActiveSupport::JSON。有关更多信息,请参阅
  • https://guides.rubyonrails.org/v7.1/configuring.html#config-active-support-message-serializer
  • 萨巴·凯埃大卫·巴克利乔纳森·海夫纳
  • 使用时将默认序列化器ActiveSupport::MessageEncryptor从 from
  • Marshal更改为.ActiveSupport::JSONconfig.load_defaults 7.1
  • Marshal仍然可以读取序列化的消息,但新消息将
  • 使用 序列化ActiveSupport::JSON。有关更多信息,请参阅
  • https://guides.rubyonrails.org/v7.1/configuring.html#config-active-support-message-serializer
  • 扎克·德沃马丁·金格拉斯乔纳森·赫夫纳
  • ActiveSupport::TestCase#stub_const在收益持续时间内向存根添加一个常量。
  • DHH
  • 修复ActiveSupport::EncryptedConfiguration与 Psych 4 兼容
  • 史蒂芬·萨格登
  • 改进File.atomic_write错误处理
  • 丹尼尔·佩珀
  • 修复Class#descendantsDescendantsTracker#descendants兼容 Ruby 3.1。
  • 本机Class#descendants在 Ruby 3.1 发布之前已恢复
  • Class#subclasses仍被保留,从而破坏了功能检测。
  • 让·布西耶

主动模型

  • 删除用户面临的错误消息的排版更改。
  • 例如,“不能为空”又是“不能为空”。
  • 拉斐尔·门东萨·弗朗萨
  • 支持复合标识符to_key
  • to_key避免将#id值包装到Array数组#id
  • 尼基塔·华西列夫斯基
  • 添加ActiveModel::Conversion.param_delimiter到配置中使用的分隔符to_param
  • 尼基塔·华西列夫斯基
  • undefine_attribute_methods取消定义别名属性方法和属性方法。
  • 尼基塔·华西列夫斯基
  • Error.full_message 现在从消息中删除“:base”。
  • 扎克
  • 添加一个加载钩子ActiveModel::Model(named active_model) 以匹配加载钩子
  • ActiveRecord::Base并允许覆盖该类的ActiveModel::Model各个方面。
  • 刘易斯·巴克利
  • 改进 ActiveModel::SecurePassword 中的密码长度验证,以考虑 BCrypt
  • 兼容性的字节大小。
  • 之前的密码长度验证仅考虑字符数,这可能无法
  • 准确反映 BCrypt 施加的 72 字节大小限制。此更改更新了验证
  • 以考虑字符计数和字节大小,同时保持字符长度验证不变。
user = User.new(password: "a" * 73)  # 73 characters
user.valid? # => false
user.errors[:password] # => ["is too long"]
user = User.new(password: "あ" * 25)  # 25 characters, 75 bytes
user.valid? # => false
user.errors[:password] # => ["is too long"]
  • 吉列尔莫·伊瓜兰ChatGPT
  • has_secure_password现在生成一个方法,该方法返回 用于计算密码摘要的#{attribute}_salt盐。
  • 每当密码更改时,盐就会更改,
  • 因此可以使用它来创建一次性密码重置令牌generates_token_for
class User < ActiveRecord::Base
  has_secure_password
  generates_token_for :password_reset, expires_in: 15.minutes do
    password_salt&.last(10)
  end
end
  • 拉萨罗·尼克松
  • 改进用户面临的错误消息的排版。在英语缩写中,
  • Unicode 撇号 ( U+0027) 现在是右单引号
  • U+2019)。例如,“不能为空”现在为“不能为空”。
  • 乔恩·杜弗雷纳
  • 将类添加到ActiveModel::MissingAttributeError错误消息中。
  • 在错误消息中显示哪个类缺少该属性:
user = User.first
user.pets.select(:id).first.user_id
# => ActiveModel::MissingAttributeError: missing attribute 'user_id' for Pet
  • 佩特里克·德赫斯
  • 加注以避免不可预测的NoMethodError结果 。ActiveModel::Type::Value#as_json
  • 瓦西里·埃尔莫洛维奇
  • 从 Active Model 内置类型继承且不
  • 重写该方法的自定义属性类型现在将 在序列化数据库属性值时serialize受益于优化。
  • 例如,使用如下所示的自定义类型:
class DowncasedString < ActiveModel::Type::String
  def cast(value)
    super&.downcase
  end
end
ActiveRecord::Type.register(:downcased_string, DowncasedString)
class User < ActiveRecord::Base
  attribute :email, :downcased_string
end
user = User.new(email: "FooBar@example.com")
  • 序列化email数据库的属性大约会
  • 快两倍。成本更高的cast运营可能会得到更大的改进。
  • 乔纳森·赫夫纳
  • has_secure_password现在支持通过
  • password_challenge访问器和验证进行密码挑战。
  • 密码质询是一种验证当前用户是否
  • 确实是密码所有者的安全措施。
  • 它可以在更改敏感模型字段(例如密码本身)时使用。它与密码
  • 确认不同,密码确认用于防止密码拼写错误。
  • 设置后password_challenge,验证会检查值的
  • 摘要是否与当前保留的 password_digest(即
  • password_digest_was)相匹配。
  • 这允许将密码质询作为典型呼叫的一部分来完成update
  • ,就像密码确认一样。它还允许
  • 以与其他验证错误相同的方式处理密码质询错误。
  • 例如,在控制器中,而不是:
password_params = params.require(:password).permit(
  :password_challenge,
  :password,
  :password_confirmation,
)
password_challenge = password_params.delete(:password_challenge)
@password_challenge_failed = !current_user.authenticate(password_challenge)
if !@password_challenge_failed && current_user.update(password_params)
  # ...
end
  • 你现在可以写:
password_params = params.require(:password).permit(
  :password_challenge,
  :password,
  :password_confirmation,
).with_defaults(password_challenge: "")
if current_user.update(password_params)
  # ...
end
  • 而且,在视图中,@password_challenge_failed您可以像处理 其他表单字段一样
  • 为该字段呈现错误,而不是检查 ,包括利用.password_challenge
  • config.action_view.field_error_proc
  • 乔纳森·赫夫纳
  • 支持LengthValidator:in/:within选项的无限范围
validates_length_of :first_name, in: ..30
  • 法特科迪马
  • 向包容性/排他性验证器添加对无头范围的支持:
validates_inclusion_of :birth_date, in: -> { (..Date.today) }
validates_exclusion_of :birth_date, in: -> { (..Date.today) }
  • 博·珍尼斯
  • 让验证器接受没有记录参数的 lambda
# Before
validates_comparison_of :birth_date, less_than_or_equal_to: ->(_record) { Date.today }
# After
validates_comparison_of :birth_date, less_than_or_equal_to: -> { Date.today }
  • 法特科迪马
  • 修复将长字符串投射到DateTimeor 的问题DateTime
  • 法特科迪马
  • 对代理调用使用不同的缓存命名空间
  • 目前,模型可以对相同的方法
  • 名称具有不同的属性体,从而导致冲突。添加新的命名空间:active_model_proxy
  • 可以解决该问题。
  • 克里斯·萨尔茨伯格

活动记录

  • rails db:drop运行时删除 -shm 和 -wal SQLite 文件。
  • 尼克拉斯·豪瑟勒
  • 恢复更改以引发 同一类中的关联多次声明的ArgumentError时间。#accepts_nested_attributes_for
  • #accepts_nested_attributes_for恢复的行为打破了在关注点中定义 以及
  • 在包含关注点的类中覆盖的情况。
  • 拉斐尔·门东萨·弗朗萨
  • 更好的命名以支持唯一约束。
  • 命名唯一键会导致误解它是唯一索引的简写。
  • 仅仅将其命名为唯一约束并不会产生误导。
  • 在 Rails 7.1.0.beta1 或之前版本中:
add_unique_key :sections, [:position], deferrable: :deferred, name: "unique_section_position"
remove_unique_key :sections, name: "unique_section_position"
  • 现在:
add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_section_position"
remove_unique_constraint :sections, name: "unique_section_position"
  • 上园龙太
  • 修复使用 MySQL 时模式转储中检查约束表达式的重复引用
  • 带有已包含引号的表达式的检查约束会导致
  • mysql2 适配器的模式转储无效。
  • 修复#42424
  • 菲利克斯·切林
  • 性能调整 SQLite3 适配器连接配置
  • 对于 Rails 应用程序,正常同步模式下的 Write-Ahead-Log 具有上限的日志大小、健康的共享内存缓冲区和共享缓存,平均性能会提高 2 倍。
  • 斯蒂芬·马格海姆
  • 允许 SQLite3busy_handler配置简单的最大数量retries
  • 对于性能敏感的应用程序来说,立即重试繁忙的连接是首选做法。添加对整数的支持database.yml retries,该整数在简单busy_handler函数中使用,以重试繁忙的连接,而无需指数退避,最多可达最大数量retries
  • 斯蒂芬·马格海姆
  • SQLite3 适配器现在支持supports_insert_returning?
  • 实现完整的supports_insert_returning?契约意味着 SQLite3 适配器支持自动填充列 ( #48241 ) 以及自定义主键。
  • 斯蒂芬·马格海姆
  • 确保 SQLite3 适配器使用||连接运算符处理默认函数
  • 以前,此默认函数将生成静态字符串"'Ruby ' || 'on ' || 'Rails'"
  • 现在,适配器将正确接收并使用"Ruby on Rails".
change_column_default "test_models", "ruby_on_rails", -> { "('Ruby ' || 'on ' || 'Rails')" }
  • 斯蒂芬·马格海姆
  • 将 PostgreSQL 架构转储为架构转储的一部分。
  • 拉克兰·西尔维斯特
  • 现在支持support_unencrypted_data按属性设置加密。
  • 您现在可以选择退出support_unencrypted_data特定的加密属性。
  • 这仅在以下情况下有效ActiveRecord::Encryption.config.support_unencrypted_data == true
class User < ActiveRecord::Base
  encrypts :name, deterministic: true, support_unencrypted_data: false
  encrypts :email, deterministic: true
end
  • 亚历克斯·吉库列斯库
  • 为 Active Record 事务添加检测
  • 允许订阅交易事件以进行跟踪/检测。事件负载包含连接和结果(提交、回滚、重新启动、不完整)以及计时详细信息。
ActiveSupport::Notifications.subscribe("transaction.active_record") do |event|
  puts "Transaction event occurred!"
  connection = event.payload[:connection]
  puts "Connection: #{connection.inspect}"
end
  • 丹尼尔·科尔森伊恩·坎迪
  • 通过迁移助手支持复合外键。
# Assuming "carts" table has "(shop_id, user_id)" as a primary key.<
>>
add_foreign_key(:orders, :carts, primary_key: [:shop_id, :user_id])
remove_foreign_key(:orders, :carts, primary_key: [:shop_id, :user_id])
foreign_key_exists?(:orders, :carts, primary_key: [:shop_id, :user_id])
  • 法特科迪马
  • if_not_exists添加对添加检查约束时的支持。
add_check_constraint :posts, "post_type IN ('blog', 'comment', 'share')", if_not_exists: true
  • 科迪·卡特勒
  • ArgumentError当为 同一类#accepts_nested_attributes_for中的关联声明多次时引发。
  • 以前,最后一个声明会默默地覆盖前一个声明。
  • 仍然允许在子类中重写。
  • 约书亚·杨
  • 弃用rewhere关于 的争论#merge
  • rewhereon 的参数已#merge被弃用且没有替换,并将
  • 在 Rails 7.2 中删除。
  • 亚当·赫斯
  • 修复 unscope 在特定情况下不起作用的问题
  • 前:
Post.where(id: 1...3).unscope(where: :id).to_sql # "SELECT `posts`.* FROM `posts` WHERE `posts`.`id` >= 1 AND `posts`.`id` < 3"
  • 后:
Post.where(id: 1...3).unscope(where: :id).to_sql # "SELECT `posts`.* FROM `posts`"
  • 修复#48094
  • 畠中一也
  • 将默认值更改has_secure_tokenon: :initialize
  • 将新的默认值从 更改on: :createon: :initialize
  • 可以通过配置来控制config.active_record.generate_secure_token_on
config.active_record.generate_secure_token_on = :create
  • 肖恩·多伊尔
  • 修复使用 7.0+ 迁移和 SQLite时change_column未对precision: 6列进行设置的问题 。datetime
  • 哈特利·麦奎尔
  • 支持复合标识符to_key
  • to_key避免将#id值包装到Array数组#id
  • 尼基塔·华西列夫斯基
  • 添加验证选项enum
class Contract < ApplicationRecord
  enum :status, %w[in_progress completed], validate: true
end
Contract.new(status: "unknown").valid? # => false
Contract.new(status: nil).valid? # => false
Contract.new(status: "completed").valid? # => true
class Contract < ApplicationRecord
  enum :status, %w[in_progress completed], validate: { allow_nil: true }
end
Contract.new(status: "unknown").valid? # => false
Contract.new(status: nil).valid? # => true
Contract.new(status: "completed").valid? # => true
  • 埃德姆·托普佐夫,上园龙太
  • 允许批处理方法使用已加载的关系(如果可用)
  • 对已加载的关系调用批处理方法将使用先前加载的记录,而不是
  • 再次从数据库中检索它们。
  • 亚当·赫斯
  • read_attribute(:id)如果主键不是 ,则不推荐返回主键:id
  • 从 Rails 7.2 开始,无论模型的 主键read_attribute(:id)如何,都将返回 id 列的值。
  • 要检索主键的值,请#id改用。read_attribute(:id)对于复合
  • 主键模型现在将返回 id 列的值。
  • 张钧宁
  • 修复change_table6.1 迁移的设置日期时间精度
  • 哈特利·麦奎尔
  • 修复 6.1 迁移的change_column 设置日期时间精度
  • 哈特利·麦奎尔
  • 添加ActiveRecord::Base#id_value别名以访问记录 id 列的原始值。
  • 此别名仅适用于声明:id列的模型。
  • 张钧宁
  • ActiveRecord::Store修复使用具有 JSON 结构化数据库类型的列时的先前更改跟踪
  • 之前,如果存储被定义为具有 JSON 结构化数据库类型的列#saved_change_to_key?#saved_change_to_key则访问上次保存期间所做的更改的方法#key_before_last_save将不起作用store_accessor
  • 罗伯特·迪马蒂诺
  • 完全支持NULLS [NOT] DISTINCTPostgreSQL 15+ 索引。
  • 之前的工作是允许在迁移中创建索引,但
  • schema.rb 中不支持它。此外,匹配的顺序NULLS [NOT] DISTINCT
  • 正确,这可能导致架构检测不一致。
  • 格雷戈里·琼斯
  • sanitize_sql_*使用命名绑定变量时,允许在方法中转义文字冒号字符
  • 贾斯汀·布尔
  • 修复#previously_new_record?以针对已销毁的记录返回 true。
  • 以前,如果一条记录被创建然后被销毁,#previously_new_record?则会返回 true。
  • 现在,对记录的任何 UPDATE 或 DELETE 都被视为更改,并将导致#previously_new_record?
  • 返回 false。
  • 张钧宁
  • 指定回调has_secure_token
class User < ApplicationRecord
  has_secure_token on: :initialize
end
User.new.token # => "abc123...."
  • 肖恩·多伊尔
  • 修复关联重叠时内存计数器缓存的增量
  • 当两个关联具有类似名称的计数器缓存列时,Active Record
  • 有时可能会增加错误的计数器缓存列。
  • 雅各布·贝斯基,让·布西耶
  • 不要泄露 Active Record 的秘密Cipher::Aes256Gcm#inspect
  • 前:
ActiveRecord::Encryption::Cipher::Aes256Gcm.new(secret).inspect
"#<ActiveRecord::Encryption::Cipher::Aes256Gcm:0x0000000104888038 ... @secret=\"\\xAF\\bFh]LV}q\\nl\\xB2U\\xB3 ... >"
  • 后:
ActiveRecord::Encryption::Cipher::Aes256Gcm(secret).inspect
"#<ActiveRecord::Encryption::Cipher::Aes256Gcm:0x0000000104888038>"
  • 佩特里克·德赫斯
  • 带回非本地返回时提交事务的历史行为。
Model.transaction do
  model.save
  return
  other_model.save # not executed
end
  • 从历史上看,只有引发的错误才会触发回滚,但在 Ruby 中2.3timeout
  • 开始使用throw中断执行,这会对提交打开的事务产生不利影响。
  • 为了解决这个问题,在 Active Record 6.1 中,行为被更改为回滚事务,因为它
  • 比潜在提交不完整的事务更安全。
  • 从 Rails 6.1 开始,在块内使用return,break或基本上已被弃用。throwtransaction
  • 然而,随着 的发布timeout 0.4.0Timeout.timeoutnow 再次引发错误,Active Record 能够
  • 恢复到其原始的、不那么令人惊讶的行为。
  • 现在可以通过以下方式选择加入这种历史行为:
Rails.application.config.active_record.commit_transaction_on_non_local_return = true
  • 并且是在 Rails 7.1 中创建的新应用程序的默认设置。
  • 让·布西耶
  • 弃用name关于 的争论#remove_connection
  • name参数已被弃用#remove_connection且无需替换。#remove_connection应直接在建立连接的类上调用。
  • 艾琳·M·乌奇泰尔
  • 通过逆向奇异构建来修复 has_one 。
  • 允许通过与 inverse 的单一关联从与 has_one 的关联构建记录
  • 。对于通过关联的belongs_to,
  • 不需要将外键链接到主键模型。
  • 对于 has_one,由于关联不可变,我们无法构建记录。
  • 甘农·麦吉本
  • 启用查询日志时禁用数据库准备语句
  • 准备好的语句和查询日志是不兼容的功能,因为查询日志使每个查询都是唯一的。
  • 让·布西尔·扎克
  • 支持解密使用 SHA1 哈希摘要非确定性加密的数据。
  • 这添加了一个新的 Active Record 加密选项,以支持解密
  • 使用 SHA1 哈希摘要非确定性加密的数据:
Rails.application.config.active_record.encryption.support_sha1_for_non_deterministic_encryption = true
  • 新选项解决了从 7.0 升级到 7.1 时的问题。由于 Active Record
  • 加密的初始化方式存在错误,用于非确定性加密的密钥提供程序使用
  • SHA-1 作为其摘要类,而不是由 Rails 通过 全局配置的摘要类
  • Rails.application.config.active_support.key_generator_hash_digest_class
  • 卡杜·里贝罗和豪尔赫·曼鲁比亚
  • 添加了用于枚举重命名、添加值和重命名值的 PostgreSQL 迁移命令。
  • rename_enum并且rename_enum_value是可逆的。由于 Postgres 的
  • 限制,add_enum_value这是不可逆的,因为您无法删除枚举
  • 值。作为替代方案,您应该完全删除并重新创建枚举。
rename_enum :article_status, to: :article_state
add_enum_value :article_state, "archived" # will be at the end of existing values
add_enum_value :article_state, "in review", before: "published"
add_enum_value :article_state, "approved", after: "in review"
rename_enum_value :article_state, from: "archived", to: "deleted"
  • 雷·法迪斯
  • 允许从模式派生复合主键
  • 使用包含复合主键的架构启动应用程序
  • 将不会发出警告,也不会再nil验证该ActiveRecord::Base#primary_key值。
  • 给定一个travel_routes表定义和一个TravelRoute模型,例如:
create_table :travel_routes, primary_key: [:origin, :destination], force: true do |t|
  t.string :origin
  t.string :destination
end
class TravelRoute < ActiveRecord::Base; end
  • TravelRoute.primary_key值将自动导出为["origin", "destination"]
  • 尼基塔·华西列夫斯基
  • 包括connection_pool从适配器引发的异常。
  • 提供connection_pool添加的上下文,例如导致异常的连接
  • 以及哪个角色和分片。
  • 卢安·维埃拉
  • find_each支持、find_in_batches和的多列排序in_batches
  • 对复合主键的表执行 find_each/find_in_batches/in_batches 时,可以选择每个键的升序或降序。
Person.find_each(order: [:desc, :asc]) do |person|
  person.party_all_night!
end
  • 栗本拓也
  • 修复 where on 与 has_one/has_many 多态关系的关联。
  • 前:
Treasure.where(price_estimates: PriceEstimate.all)
#=> SELECT (...) WHERE "treasures"."id" IN (SELECT "price_estimates"."estimate_of_id" FROM "price_estimates")
  • 之后:
Treasure.where(price_estimates: PriceEstimate.all)
#=> SELECT (...) WHERE "treasures"."id" IN (SELECT "price_estimates"."estimate_of_id" FROM "price_estimates" WHERE "price_estimates"."estimate_of_type" = 'Treasure')
  • 拉萨罗·尼克松
  • 在创建 Active Record 记录时分配自动填充的列。
  • 更改记录创建逻辑,以允许在创建后立即auto_increment分配列
  • ,无论其与模型主键的关系如何。
  • PostgreSQL 适配器从这一更改中获益最多,该更改允许在使用语句插入行后立即在对象上分配任意数量的自动填充列RETURNING
  • 尼基塔·华西列夫斯基
  • 使用的shards哈希值中的第一个键。connected_todefault_shard
  • 某些应用程序可能不想:default在其连接模型中用作分片名称。不幸的是,Active Record 期望存在一个:default分片,因为它必须假设一个分片才能从池管理器获得正确的连接。无需强制应用程序手动设置此值,而是connects_to可以从分片的哈希值推断默认分片名称,并且现在假设第一个分片是您的默认分片。
  • 例如,如果您的模型如下所示:
class ShardRecord < ApplicationRecord
  self.abstract_class = true
  connects_to shards: {
    shard_one: { writing: :shard_one },
    shard_two: { writing: :shard_two }
  }
  • 那么default_shard这个类的 就会被设置为shard_one
  • 修复:#45390
  • 艾琳·M·乌奇泰尔
  • 修复二进制列支持的序列化属性的突变检测。
  • 让·布西耶
  • 添加ActiveRecord.disconnect_all!方法以立即关闭所有池中的所有连接。
  • 让·布西耶
  • 丢弃事务中可能留下的连接。
  • 在某些情况下,由于错误,within_new_transaction可能会意外地在打开的事务中留下连接。在这些情况下,连接可能会被重用,并且可能会发生以下情况:
  • 写入实际上成功了,但看起来却失败了。
  • 写入看似成功,但实际上却失败了。
  • 读取返回陈旧或未提交的数据。
  • 此前,曾发现以下情况:
  • 事务期间遇到错误,然后在尝试回滚事务时遇到另一个错误。
  • 现在,又检测到以下病例:
  • 成功开始事务后立即遇到错误。
  • 提交事务时遇到错误,然后在尝试回滚事务时遇到另一个错误。
  • 回滚事务时遇到错误。
  • 尼克·道尔
  • Active Record 查询缓存现在会逐出最近最少使用的条目
  • 默认情况下,它只保留100最近使用的查询。
  • 缓存大小可以通过配置database.yml
development:
  adapter: mysql2
  query_cache: 200
  • 它也可以完全禁用:
development:
  adapter: mysql2
  query_cache: false
  • 让·布西耶
  • 弃用check_pending!赞成check_all_pending!
  • check_pending!只会检查当前数据库连接或传入的数据库连接上的挂起迁移。此功能已被弃用,取而代之的是check_all_pending!它将查找给定环境中数据库配置的所有挂起迁移。
  • 艾琳·M·乌奇泰尔
  • 制作increment_counter/decrement_counter接受金额参数
Post.increment_counter(:comments_count, 5, by: 3)
  • 法特科迪马
  • Array#intersect?添加对to的支持ActiveRecord::Relation
  • Array#intersect?仅适用于 Ruby 3.1 或更高版本。
  • 这使得 RubocopStyle/ArrayIntersect警察能够处理ActiveRecord::Relation物体。
  • 约翰·哈里·凯利
  • 可延迟外键可以传递给t.references.
  • 石井弘之
  • 弃用deferrable: true的选项add_foreign_key
  • deferrable: true已弃用,取而代之的是deferrable: :immediate,并将
  • 在 Rails 7.2 中删除。
  • 因为deferrable: truedeferrable: :deferred很难理解。
  • true 和 :deferred 都是真实值。此行为与#46192
  • 中添加的 add_unique_key 方法的可延迟选项相同。
  • 石井弘之
  • AbstractAdapter#execute现在#exec_query清除查询缓存
  • 如果您需要执行只读 SQL 查询而不清除查询
  • 缓存,请使用AbstractAdapter#select_all.
  • 让·布西耶
  • 制作.joins/.left_outer_joins使用 CTE。
  • 例如:
Post
 .with(commented_posts: Comment.select(:post_id).distinct)
 .joins(:commented_posts)
#=> WITH (...) SELECT ... INNER JOIN commented_posts on posts.id = commented_posts.post_id
  • 弗拉基米尔·杰门捷夫
  • ActiveRecord::ConnectionAdapters::Mysql2Adapter
  • 为(named )添加一个加载钩子active_record_mysql2adapter以允许覆盖该类的
  • ActiveRecord::ConnectionAdapters::Mysql2Adapter各个方面。这使得与已有的负载挂钩Mysql2Adapter
  • 保持一致。PostgreSQLAdapterSQLite3Adapter
  • 法特科迪马
  • 引入 Trilogy 数据库客户端适配器
  • Trilogy 是一个兼容 MySQL 的数据库客户端。
  • Rails 应用程序可以通过配置以下内容来使用 Trilogy config/database.yml
development:
adapter: trilogy
database: blog_development
pool: 5
  • 或者通过使用DATABASE_URL环境变量:
ENV['DATABASE_URL'] # => "trilogy://localhost/blog_development?pool=5"
  • 张钧宁
  • after_commit模型上定义的回调现在以正确的顺序执行。
class User < ActiveRecord::Base
  after_commit { puts("this gets called first") }
  after_commit { puts("this gets called second") }
end
  • 以前,回调以相反的顺序执行。要选择接受新行为:
config.active_record.run_after_transaction_callbacks_in_order_defined = true
  • 这是新应用程序的默认设置。
  • 亚历克斯·吉库列斯库
  • 推断和关联foreign_key何时出现。inverse_ofhas_onehas_many
has_many :citations, foreign_key: "book1_id", inverse_of: :book
  • 可以简化为
has_many :citations, inverse_of: :book
  • 并且foreign_key将从相应的belongs_to关联中读取。
  • 丹尼尔惠特尼
  • 限制自动生成的索引名称的最大长度
  • 自动生成的索引名称现在限制为 62 字节,符合
  • MySQL、Postgres 和 SQLite 的默认索引名称长度限制。
  • 任何超过限制的索引名称都将回退到新的短格式。
  • 不久):
index_testings_on_foo_and_bar_and_first_name_and_last_name_and_administrator
  • 之后(短格式):
idx_on_foo_bar_first_name_last_name_administrator_5939248142
  • 短格式包含一个哈希值,以确保名称在数据库范围内是唯一的。
  • 迈克·库特马什
  • 为 Active Record 模型引入更稳定、更优化的 Marshal 序列化器。
  • 可以通过 启用config.active_record.marshalling_format_version = 7.1
  • 让·布西耶
  • 允许使用列元组语法指定 where 子句。
  • 现在的查询#where接受一个新的元组语法,该语法接受
  • 一个列数组作为键,以及一个相应元组数组作为值。
  • 键指定列列表,而值是
  • 符合列列表的有序元组数组。
  • 例如:
# Cpk::Book => Cpk::Book(author_id: integer, number: integer, title: string, revision: integer)
# Cpk::Book.primary_key => ["author_id", "number"]
book = Cpk::Book.create!(author_id: 1, number: 1)
Cpk::Book.where(Cpk::Book.primary_key => [[1, 2]]) # => [book]
# Topic => Topic(id: integer, title: string, author_name: string...)
Topic.where([:title, :author_name] => [["The Alchemist", "Paulo Coelho"], ["Harry Potter", "J.K Rowling"]])
  • 帕斯·马丹
  • 报告 SQL 警告时允许忽略警告代码。
  • 可以忽略警告代码的 Active Record 配置
# Configure allowlist of warnings that should always be ignored
config.active_record.db_warnings_ignore = [
  "1062", # MySQL Error 1062: Duplicate entry
]
  • MySQL 和 PostgreSQL 适配器支持此功能。
  • 尼克·博罗梅奥
  • 引入:active_record_fixtures延迟加载钩子。
  • TestFixtures只要包含
  • 在类中,就会运行用此名称定义的挂钩。
ActiveSupport.on_load(:active_record_fixtures) do
  self.fixture_paths << "test/fixtures"
end
klass = Class.new
klass.include(ActiveRecord::TestFixtures)
klass.fixture_paths # => ["test/fixtures"]
  • 安德鲁·诺沃塞拉克
  • 介绍一下TestFixtures#fixture_paths
  • 现在可以使用访问器指定多个夹具路径#fixture_paths。默认情况下,
  • 应用程序将继续将其作为其唯一的固定路径, 但可以指定其他固定路径。test/fixtures
ActiveSupport::TestCase.fixture_paths << "component1/test/fixtures"
ActiveSupport::TestCase.fixture_paths << "component2/test/fixtures"
  • TestFixtures#fixture_path现已弃用。
  • 安德鲁·诺沃塞拉克
  • 添加对 PostgreSQL 中可延迟排除约束的支持。
  • 默认情况下,PostgreSQL 中的排除约束会在每个语句后进行检查。 这适用于大多数用例,但在使用多个语句
  • 替换具有重叠范围的记录时成为主要限制。
exclusion_constraint :users, "daterange(valid_from, valid_to) WITH &&", deferrable: :immediate
  • 在每个语句之后传递deferrable: :immediate检查约束,但允许 在事务中
  • 使用手动推迟检查。SET CONSTRAINTS ALL DEFERRED
  • 这将导致交易后检查排除项。
  • 还可以将默认行为从立即检查
  • (在语句之后)更改为延迟检查(在事务之后):
exclusion_constraint :users, "daterange(valid_from, valid_to) WITH &&", deferrable: :deferred
  • 石井弘之
  • 尊重for方法foreign_type的选项。delegated_type{role}_class
  • 现在可以使用选项指定delegated_type非常规列名的用法。此选项与转发到包装的底层关联 相同。{role}_typeforeign_type
  • foreign_typebelongs_todelegated_type
  • 贾森·卡恩斯
  • 添加对唯一约束的支持(仅限 PostgreSQL)。
add_unique_key :sections, [:position], deferrable: :deferred, name: "unique_section_position"
remove_unique_key :sections, name: "unique_section_position"
  • 有关唯一约束的更多信息,请参阅 PostgreSQL 的唯一约束文档。
  • 默认情况下,PostgreSQL 中的唯一约束会在每个语句后进行检查。 这适用于大多数用例,但在使用多个语句用唯一列
  • 替换记录时成为主要限制。
  • 在记录之间交换唯一列的示例。
# position is unique column
old_item = Item.create!(position: 1)
new_item = Item.create!(position: 2)
Item.transaction do
  old_item.update!(position: 2)
  new_item.update!(position: 1)
end
  • 使用默认行为,事务将在执行
  • 第一条UPDATE语句时失败。
  • 通过将:deferrable选项传递给迁移add_unique_key中的语句
  • ,可以推迟此检查。
add_unique_key :items, [:position], deferrable: :immediate
  • 通过deferrable: :immediate不会改变前面示例的行为,但允许在事务中
  • 使用手动推迟检查。 这将导致在交易后检查唯一约束。SET CONSTRAINTS ALL DEFERRED
  • 还可以将默认行为从立即
  • 检查(在语句之后)调整为延迟检查(在事务之后):
add_unique_key :items, [:position], deferrable: :deferred
  • 如果要将现有唯一索引更改为可延迟唯一索引,可以使用 :using_index
  • 创建可延迟唯一约束。
add_unique_key :items, deferrable: :deferred, using_index: "index_items_on_position"
  • 石井弘之
  • 删除已弃用的Tasks::DatabaseTasks.schema_file_type.
  • 拉斐尔·门东萨·弗朗萨
  • 删除已弃用的config.active_record.partial_writes.
  • 拉斐尔·门东萨·弗朗萨
  • 删除已弃用的ActiveRecord::Base配置访问器。
  • 拉斐尔·门东萨·弗朗萨
  • :include_replicas从 中删除参数configs_for。请:include_hidden改用论证。
  • 艾琳·M·乌奇泰尔
  • 允许应用程序通过自定义哈希键查找配置。
  • 如果您已经注册了自定义配置或想要查找哈希与特定键匹配的配置,现在您可以传递config_keyconfigs_for. 例如,如果您有一个db_config密钥,vitess您可以通过匹配该密钥来查找数据库配置哈希。
ActiveRecord::Base.configurations.configs_for(env_name: "development", name: "primary", config_key: :vitess)
ActiveRecord::Base.configurations.configs_for(env_name: "development", config_key: :vitess)
  • 艾琳·M·乌奇泰尔
  • 允许应用程序注册自定义数据库配置处理程序。
  • 添加一种机制,用于在您希望数据库配置响应自定义方法的情况下注册自定义处理程序。这对于非 Rails 数据库适配器或 Vitess 等工具非常有用,您可能希望以不同于标准HashConfigUrlConfig.
  • 给定以下数据库 YAML,我们希望animals数据库创建一个CustomConfig对象,而primary数据库将是 a UrlConfig
development:
  primary:
    url: postgres://localhost/primary
  animals:
    url: postgres://localhost/animals
    custom_config:
      sharded: 1
  • 要注册自定义处理程序,首先创建一个具有自定义方法的类:
class CustomConfig < ActiveRecord::DatabaseConfigurations::UrlConfig
  def sharded?
    custom_config.fetch("sharded", false)
  end
  private
    def custom_config
      configuration_hash.fetch(:custom_config)
    end
end
  • 然后在初始化程序中注册配置:
ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
  next unless config.key?(:custom_config)
  CustomConfig.new(env_name, name, url, config)
end
  • 当应用程序启动时,带有:custom_config密钥的配置哈希将是CustomConfig对象并响应sharded?. 应用程序必须处理 Active Record 应使用其自定义处理程序的情况。
  • 艾琳·M·乌奇特尔和约翰·克雷佩齐
  • ActiveRecord::Base.serialize默认情况下不再使用 YAML。
  • YAML 的性能不是特别好,
  • 如果使用不当,可能会导致安全问题。
  • 不幸的是,Ruby 的 stdlib 中实际上没有任何好的序列化程序
  • 可以替代它。
  • 显而易见的选择是 JSON,对于此用例来说,这是一种很好的格式,
  • 但是 Ruby 的 stdlib 中的 JSON 序列化器不够严格,因为它会回退到将
  • 未知类型转换为字符串,这可能会导致数据损坏。
  • 一些第三方 JSON 库Oj有合适的严格模式。
  • 因此,用户最好根据自己的限制选择序列化器。
  • 可以通过设置恢复原来的默认值config.active_record.default_column_serializer = YAML
  • 让·布西耶
  • ActiveRecord::Base.serialize签名改变了。
  • 现在接受两个不同的关键字参数,而不是接受两种可能
  • 类型的值的单个位置参数。serialize
  • 前:
serialize :content, JSON
serialize :backtrace, Array
  • 后:
serialize :content, coder: JSON
serialize :backtrace, type: Array
  • 让·布西耶
  • 如果可用,则使用 YAML 列YAML.safe_dump
  • 截至目前psych 5.1.0YAML.safe_dump现在可以应用相同的允许
  • 类型限制YAML.safe_load
  • 当我们第一次尝试序列化有效负载时,最好确保有效负载仅使用允许的类型
  • ,否则最终可能会在数据库中出现无效记录
  • 让·布西耶
  • ActiveRecord::QueryLogs更好地处理损坏的编码。
  • 使用 BLOB 字段构建查询来包含二进制数据时,这种情况并不罕见。除非调用仔细地将字符串编码为 ASCII-8BIT,否则
  • 它通常最终会被编码为UTF-8,并且最终QueryLogs
  • 失败。
  • ActiveRecord::QueryLogs不再依赖于正确编码的查询。
  • 让·布西耶
  • ActiveRecord::Generators::ModelGenerator修复不尊重 create_table_migration 模板覆盖的错误。
rails g model create_books title:string content:text
  • 现在将从以下位置的 create_table_migration.rb.tt 模板中按顺序读取:
lib/templates/active_record/model/create_table_migration.rb
lib/templates/active_record/migration/create_table_migration.rb
  • 斯宾塞·内斯特
  • ActiveRecord::Relation#explain现在接受选项。
  • 对于支持它们的数据库和适配器(当前是 PostgreSQL
  • 和 MySQL),可以传递选项explain以提供更
  • 详细的查询计划分析:
Customer.where(id: 1).joins(:orders).explain(:analyze, :verbose)
  • 里德·林奇
  • 现在可以将多个Arel::Nodes::SqlLiteral节点添加在一起以
  • 形成Arel::Nodes::Fragments节点。这允许连接多个
  • SQL 片段。
  • 马修·德雷珀奥勒·弗里斯
  • ActiveRecord::Base#signed_id如果调用新记录则加注。
  • 以前它会返回一个不可用的 ID,因为它基于id = nil.
  • 亚历克斯·吉库列斯库
  • 允许报告 SQL 警告。
  • 可以设置 Active Record 配置来启用 SQL 警告报告。
# Configure action to take when SQL query produces warning
config.active_record.db_warnings_action = :raise
# Configure allowlist of warnings that should always be ignored
config.active_record.db_warnings_ignore = [
  /Invalid utf8mb4 character string/,
  "An exact warning message",
]
  • MySQL 和 PostgreSQL 适配器支持此功能。
  • 阿德里安娜·张帕斯·马丹
  • 添加#regroup查询方法作为简写.unscope(:group).group(fields)
  • 例子:
Post.group(:title).regroup(:author)
# SELECT `posts`.`*` FROM `posts` GROUP BY `posts`.`author`
  • 丹尼尔斯·维索卡斯
  • 如果扩展必须安装在另一个模式上,PostgreSQL 适配器方法enable_extension现在允许参数为。[schema_name.]<extension_name>
  • 例子:enable_extension('heroku_ext.hstore')
  • 莱昂纳多·卢阿特
  • :include选项添加到add_index.
  • 添加对使用参数在 PostgreSQL 索引中包含非键列的支持INCLUDE
add_index(:users, :email, include: [:id, :created_at])
  • 将导致:
CREATE INDEX index_users_on_email USING btree (email) INCLUDE (id, created_at)
  • 史蒂夫·艾布拉姆斯
  • ActiveRecord::Relation、和方法采用可选的模式 参数,更接近地匹配它们的#any?等效项。#none?#one?
  • Enumerable
  • 乔治·克拉格霍恩
  • 添加ActiveRecord::Base.normalizes用于声明属性规范化。
  • 分配或
  • 更新属性时会应用属性规范化,并且规范化值将保留到数据库中。规范化
  • 还应用于查询
  • 方法的相应关键字参数,允许使用非规范化值查询记录。
  • 例如:
class User < ActiveRecord::Base
  normalizes :email, with: -> email { email.strip.downcase }
  normalizes :phone, with: -> phone { phone.delete("^0-9").delete_prefix("1") }
end
user = User.create(email: " CRUISE-CONTROL@EXAMPLE.COM\n")
user.email                  # => "cruise-control@example.com"
user = User.find_by(email: "\tCRUISE-CONTROL@EXAMPLE.COM ")
user.email                  # => "cruise-control@example.com"
user.email_before_type_cast # => "cruise-control@example.com"
User.where(email: "\tCRUISE-CONTROL@EXAMPLE.COM ").count         # => 1
User.where(["email = ?", "\tCRUISE-CONTROL@EXAMPLE.COM "]).count # => 0
User.exists?(email: "\tCRUISE-CONTROL@EXAMPLE.COM ")         # => true
User.exists?(["email = ?", "\tCRUISE-CONTROL@EXAMPLE.COM "]) # => false
User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "5558675309"
  • 乔纳森·赫夫纳
  • 隐藏对 before_commissed 的更改!标志后面的回调行为。
  • #46525中,围绕 before_commissed 的行为!回调已更改,以便回调
  • 将在事务中的每个注册记录上运行,而不仅仅是记录的第一个副本。
  • 这种行为变化现在由配置选项 控制
  • config.active_record.before_committed_on_all_records。Rails 7.1 上默认启用它。
  • 张钧宁
  • 查询日志标签namespaced_controller现在与controller格式匹配
  • 例如,处理的请求NameSpaced::UsersController现在将记录为:
:controller # "users"
:namespaced_controller # "name_spaced/users"
  • 亚历克斯·吉库列斯库
  • 仅从 ActiveRecord::Calculations#ids 返回唯一的 id
  • 更新了 ActiveRecord::Calculations#ids,以便在使用 eager_load、预加载和包含时仅返回基本模型的唯一 id 。
Post.find_by(id: 1).comments.count
# => 5
Post.includes(:comments).where(id: 1).pluck(:id)
# => [1, 1, 1, 1, 1]
Post.includes(:comments).where(id: 1).ids
# => [1]
  • 约书亚·杨
  • 停止使用列LOWER()上不区分大小写的查询citext
  • 以前,LOWER()添加了用于例如唯一性验证的
  • case_sensitive: false.
  • 文档中没有提到LOWER()
  • 在这种情况下不会使用没有的索引。
  • 菲尔·皮罗日科夫
  • AbstractMysqlAdapter 中的Extract#sync_timezone_changes方法使子类
  • 能够同步数据库时区更改而无需重写#raw_execute.
  • 阿德里安娜·张帕斯·马丹
  • 转储 sql 迁移版本时不要写入额外的新行
  • 此更改更新了insert_versions_sql函数,以便包含当前数据库迁移版本的数据库插入字符串不会以两个额外的新行结尾。
  • 米莎·施瓦茨
  • 修复composed_of值冻结和重复。
  • 以前的复合值表现出两种令人困惑的行为:
  • 当读取复合值时,它不会冻结,从而使其与其底层数据库
  • 列不同步。
  • 当写入复合值时,参数将被冻结,这可能会使调用者感到困惑。
  • 目前,基于数据库列实例化的复合值被冻结(解决第一个问题),
  • 分配的复合值被复制并冻结重复值(解决第二个问题)。
  • 格雷格·纳维斯
  • 修复列不敏感缓存的冗余更新
  • 修复了冗余查询检查列功能以进行不敏感
  • 比较的问题。
  • 菲尔·皮罗日科夫
  • 允许禁用由ActiveRecord.enum.
  • 阿尔弗雷德·多米尼克
  • belongs_to如果关联未更改,请避免验证关联。
  • 以前,在更新记录时,Active Record 将执行额外的查询来检查关联是否存在
  • belongs_to(如果将存在配置为强制),即使该属性未更改也是如此。
  • 目前,仅belongs_to检查相关列是否存在。使用这种方法可能会产生孤立记录
  • 。为了避免这个问题,您需要使用外键。
  • 此行为可以通过配置来控制:
config.active_record.belongs_to_required_validates_foreign_key = false
  • 并将默认禁用config.load_defaults 7.1
  • 法特科迪马
  • has_one关联现在 在所有者模型上belongs_to定义一个reset_association方法(其中是关联的名称)。此 方法卸载缓存的关联记录(如果有),并导致下一次访问 从数据库中查询它。
  • association
  • 乔治·克拉格霍恩
  • 允许按属性设置 YAML 允许的类(安全加载)和不安全加载。
  • 卡洛斯·帕哈雷斯
  • 添加构建持久化方法
  • 提供 的包装器new,以提供功能奇偶校验,create能够使用与关联方法 相同的符号
  • 从哈希数组创建多个记录。
  • build
  • 肖恩·丹尼
  • 分配给只读属性时引发
class Post < ActiveRecord::Base
  attr_readonly :content
end
Post.create!(content: "cannot be updated")
post.content # "cannot be updated"
post.content = "something else" # => ActiveRecord::ReadonlyAttributeError
  • 以前,分配会成功,但不会静默写入数据库。
  • 此行为可以通过配置来控制:
config.active_record.raise_on_assign_to_attr_readonly = true
  • 并将默认启用config.load_defaults 7.1
  • 亚历克斯·吉库莱斯库哈特利·麦奎尔
  • 允许取消预加载和 eager_load 关联的范围
  • 添加了取消预加载和 eager_load 关联范围的功能,就像
  • 包含、联接等一样。请参阅 ActiveRecord::QueryMethods::VALID_UNSCOPING_VALUES
  • 以获取支持的取消范围范围的完整列表。
query.unscope(:eager_load, :preload).group(:id).select(:id)
  • 大卫·莫尔豪斯
  • 在检查时添加加密属性的自动过滤
  • 该功能默认启用,但可以通过以下方式禁用
config.active_record.encryption.add_to_filter_parameters = false
  • 哈特利·麦奎尔
  • 清除 #dup 上的锁定列
  • 此更改修复了不重复的locking_column,如id 和时间戳。
car = Car.create!
car.touch
car.lock_version #=> 1
car.dup.lock_version #=> 0
  • 神谷翔一杨圣基上田亮平
  • 尽早使交易无效
  • 挽救TransactionRollbackError异常后,Rails 会使流程中较早的事务无效
  • ,从而允许框架ROLLBACK在更多情况下跳过发出语句。仅影响已配置为 的
  • 适配器, 此时仅适用于该适配器。savepoint_errors_invalidate_transactions?true
  • mysql2
  • 尼基塔·华西列夫斯基
  • ActiveRecord::Base允许配置列列表以在对象发出的 SQL 查询中使用
  • 现在可以配置列列表,用于在
  • 更新、删除或重新加载ActiveRecord::Base对象时构建 SQL 查询子句
class Developer < ActiveRecord::Base
  query_constraints :company_id, :id
end
developer = Developer.first.update(name: "Bob")
# => UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
  • 尼基塔·华西列夫斯基
  • 添加validate到 schema.rb 中的外键和检查约束
  • 以前,在添加外键或检查约束时schema.rb不会记录是否validate: false已使用
  • ,因此从架构恢复数据库可能会导致外键或检查约束验证
  • 不正确。
  • 汤米·格雷夫斯
  • 适配器#execute方法现在接受一个allow_retry选项。设置为 时true,在遇到与连接相关的错误时,将重试 SQL 语句
  • ,直至达到数据库的配置connection_retries值。
  • 张钧宁
  • after_commit :destroy当数据库行被删除时触发回调。
  • 这可以防止在同一记录上多次调用 时after_commit :destroy再次触发回调。
  • destroy
  • 本谢尔顿
  • 修复ciphertext_for尚未加密的值。
  • 以前,ciphertext_for返回尚未加密的值的明文
  • ,例如非持久记录:
Post.encrypts :body<
>>
post = Post.create!(body: "Hello")
post.ciphertext_for(:body)
# => "{\"p\":\"abc..."
post.body = "World"
post.ciphertext_for(:body)
# => "World"
  • 现在,ciphertext_for将始终返回加密属性的密文
Post.encrypts :body<
>>
post = Post.create!(body: "Hello")
post.ciphertext_for(:body)
# => "{\"p\":\"abc..."
post.body = "World"
post.ciphertext_for(:body)
# => "{\"p\":\"xyz..."
  • 乔纳森·赫夫纳
  • 修复了使用长表名的组和计数会返回不正确结果的错误。
  • 户口翔太,小野优作
  • 修复列默认值的加密。
  • 以前,使用列默认值的加密属性似乎
  • 在创建时已加密,但实际上并非如此:
Book.encrypts :name<
>>
book = Book.create!
book.name
# => "<untitled>"
book.name_before_type_cast
# => "{\"p\":\"abc..."
book.reload.name_before_type_cast
# => "<untitled>"
  • 现在,具有列默认值的属性已加密:
Book.encrypts :name<
>>
book = Book.create!
book.name
# => "<untitled>"
book.name_before_type_cast
# => "{\"p\":\"abc..."
book.reload.name_before_type_cast
# => "{\"p\":\"abc..."
  • 乔纳森·赫夫纳
  • Base弃用从到 的委派connection_handler
  • 不推荐调用Base.clear_all_connections!Base.clear_active_connections!,Base.clear_reloadable_connections!和。Base.flush_idle_connections!请直接在连接处理程序上调用这些方法。Base在未来的 Rails 版本中,从到 的委派connection_handler将被删除。
  • 艾琳·M·乌奇泰尔
  • 允许 ActiveRecord::QueryMethods#reselect 接收哈希值,类似于 ActiveRecord::QueryMethods#select
  • 桑帕特·巴德
  • 在管理迁移中的列和表时验证选项。
  • 如果将无效选项传递给诸如create_table和之类的迁移方法add_column,则会引发错误,
  • 而不是默默地忽略该选项。选项的验证仅适用于
  • 创建的新迁移。
  • 郭湘潭,乔治·万博尔德
  • 更新查询日志标签以默认使用SQLCommenter格式。参见#46179
  • 要选择退出 SQLCommenter 格式的查询日志标记,请设置config.active_record.query_log_tags_format = :legacy。默认情况下,此项设置为:sqlcommenter
  • ModulitosIheanyi
  • 创建 rake 任务时允许 database.yml 中的任何 ERB。
  • database.yml即使 ERB 访问环境配置,也可以使用任何 ERB
  • 不赞成config.active_record.suppress_multiple_database_warning
  • 艾克发送
  • 将表添加到重复列定义的错误中。
  • 如果迁移为表定义了重复列,则错误消息
  • 会显示它涉及哪个表。
  • 佩特里克·德赫斯
  • 修复虚拟日期时间列上错误的 nil 默认精度。
  • 在此更改之前,虚拟日期时间列没有
  • 与常规日期时间列相同的默认精度,导致以下内容
  • 错误地等效:
t.virtual :name, type: datetime,                 as: "expression"
t.virtual :name, type: datetime, precision: nil, as: "expression"
  • 此更改修复了默认精度查找,因此虚拟日期时间列和常规
  • 日期时间列默认精度匹配。
  • 萨姆·博斯托克
  • #with_raw_connection使用来自in的连接#quote_string
  • 这确保了字符串引用包含在提供的重新连接和重试逻辑
  • #with_raw_connection
  • 张钧宁
  • expires_at选项添加到signed_id.
  • 神谷庄一
  • 允许应用程序设置查询重试的重试期限。
  • 在#44576#44591中完成的工作的基础上,我们扩展了自动重新连接数据库连接的逻辑
  • 以考虑超时限制。如果自第一次尝试查询以来已经过去了给定的时间,我们将不会重试
  • 查询。该
  • 值默认为 nil,这意味着无论经过多少时间,所有可重试的查询都会重试,
  • 但这可以通过retry_deadline数据库配置中的选项进行更改。
  • 张钧宁
  • 修复查询缓存可能返回错误值的情况。参见#46044
  • 亚伦·帕特森
  • 支持 MySQLDatabaseTasks 的 MySQL ssl-mode 选项。
  • 验证数据库服务器的身份需要将 ssl-mode
  • 选项设置为 VERIFY_CA 或 VERIFY_IDENTITY。
  • 以前,对于创建数据库和转储结构等 MySQL 数据库任务,此选项被忽略。
  • 佩特里克·德赫斯
  • 移动ActiveRecord::InternalMetadata到一个独立的对象。
  • ActiveRecord::InternalMetadata不再继承自ActiveRecord::Base,现在是一个独立的对象,应该使用connection. 该类是私有的,不应由应用程序直接使用。如果您想与架构迁移表进行交互,请直接在连接上访问它,例如:ActiveRecord::Base.connection.schema_migration
  • 艾琳·M·乌奇泰尔
  • 不推荐ActiveSupport::Duration使用整数引用
  • 不推荐使用 ActiveSupport::Duration 作为 SQL
  • 字符串模板中的内插绑定参数。为了避免此警告,您应该显式地
  • 将持续时间转换为更具体的数据库类型。例如,如果您
  • 想使用整数秒的持续时间:
Record.where("duration = ?", 1.hour.to_i)
  • 如果要将持续时间用作 ISO 8601 字符串:
Record.where("duration = ?", 1.hour.iso8601)
  • 阿兰·格林曼
  • 允许QueryMethods#in_order_of按字符串列名称排序。
Post.in_order_of("id", [4,2,3,1]).to_a
Post.joins(:author).in_order_of("authors.name", ["Bob", "Anna", "John"]).to_a
  • 伊戈尔·卡西安丘克
  • 移动ActiveRecord::SchemaMigration到一个独立的对象。
  • ActiveRecord::SchemaMigration不再继承自ActiveRecord::Base,现在是一个独立的对象,应该使用connection. 该类是私有的,不应由应用程序直接使用。如果您想与架构迁移表进行交互,请直接在连接上访问它,例如:ActiveRecord::Base.connection.schema_migration
  • 艾琳·M·乌奇泰尔
  • 弃用all_connection_pools并使其connection_pool_list更加明确。
  • #45924 上的后续内容all_connection_pools现已弃用。connection_pool_list将扮演一个明确的角色,或者应用程序可以通过传递 来选择新的行为:all
  • 艾琳·M·乌奇泰尔
  • 修复连接处理程序方法以在所有池上操作。
  • active_connections?clear_active_connections!clear_reloadable_connections!clear_all_connections!flush_idle_connections!现在默认在所有池上运行。以前,除非指定,否则它们将默认使用current_role:writing角色。
  • 艾琳·M·乌奇泰尔
  • 允许 ActiveRecord::QueryMethods#select 接收哈希值。
  • 目前,select可能仅接收原始 sql 和符号来定义要选择的列和别名。
  • 通过此更改,我们可以提供hash参数,例如:
Post.joins(:comments).select(posts: [:id, :title, :created_at], comments: [:id, :body, :author_id])
#=> "SELECT \"posts\".\"id\", \"posts\".\"title\", \"posts\".\"created_at\", \"comments\".\"id\", \"comments\".\"body\", \"comments\".\"author_id\"
#   FROM \"posts\" INNER JOIN \"comments\" ON \"comments\".\"post_id\" = \"posts\".\"id\""
Post.joins(:comments).select(posts: { id: :post_id, title: :post_title }, comments: { id: :comment_id, body: :comment_body })
#=> "SELECT posts.id as post_id, posts.title as post_title, comments.id as comment_id, comments.body as comment_body
#    FROM \"posts\" INNER JOIN \"comments\" ON \"comments\".\"post_id\" = \"posts\".\"id\""
  • 亚历山大·霍鲁本科约瑟夫·西马内克让·布西埃
  • 适应虚拟属性ActiveRecord::Persistence#becomes
  • 当源类和目标类具有不同的属性集时,会调整
  • 属性,以便添加来自目标的额外属性。
class Person < ApplicationRecord
end
class WebUser < Person
  attribute :is_admin, :boolean
  after_initialize :set_admin
  def set_admin
    write_attribute(:is_admin, email =~ /@ourcompany\.com$/)
  end
end
person = Person.find_by(email: "email@ourcompany.com")
person.respond_to? :is_admin
# => false
person.becomes(WebUser).is_admin?
# => true
  • 雅各布·贝斯基桑普森·克劳利
  • 修复ActiveRecord::QueryMethods#in_order_of以包含nils,以匹配
  • 的行为Enumerable#in_order_of
  • 例如,Post.in_order_of(:title, [nil, "foo"])现在将包括
  • 带有nil标题的帖子,与 相同Post.all.to_a.in_order_of(:title, [nil, "foo"])
  • 法特科迪马
  • 优化add_timestamps以使用单个 SQL 语句。
add_timestamps :my_table
  • 现在生成以下 SQL:
ALTER TABLE "my_table" ADD COLUMN "created_at" datetime(6) NOT NULL, ADD COLUMN "updated_at" datetime(6) NOT NULL
  • 伊利亚娜·哈吉阿塔纳索娃
  • 添加drop_enumPostgreSQL 的迁移命令
  • 这与 的相反create_enum。在删除枚举之前,请确保已
  • 删除依赖于它的列。
  • 亚历克斯·吉库列斯库
  • if_exists在删除检查约束时添加对选项的支持。
  • remove_check_constraint方法现在接受一个if_exists选项。如果设置
  • 为 true,则在检查约束不存在时不会引发错误。
  • 玛格丽特·帕尔萨阿迪亚·不塔尼
  • find_or_create_by现在尝试查找第二次是否遇到唯一性约束。
  • find_or_create_by总是本质上很活泼,要么创建多个
  • 重复记录,要么失败,ActiveRecord::RecordNotUnique具体取决于
  • 是否设置了适当的唯一性约束。
  • create_or_find_by是为此用例引入的,但是
  • 当记录大部分时间都存在时,这是相当浪费的,因为 INSERT 需要发送
  • 比 SELECT 更多的数据,并且需要数据库进行更多工作。此外,在某些
  • 数据库上,它实际上可能会消耗主键增量,这是不希望的。
  • 因此,对于大多数情况下预计记录存在的情况,find_or_create_by
  • 可以通过重试 iffind失败create
  • 消除竞争条件ActiveRecord::RecordNotUnique。这假设该表具有适当的
  • 唯一性约束,如果没有,find_or_create_by仍然会导致重复记录。
  • 让·布西埃亚历克斯·厨房
  • 为 ActiveRecord 数据库适配器引入更简单的构造函数 API。
  • 以前,适配器必须知道如何构建新的原始连接以
  • 支持重新连接,但也期望传递初始已
  • 建立的连接。
  • 当手动创建适配器实例时,它现在将接受单个
  • 配置哈希,并且仅根据需要建立真正的连接。
  • 马修·德雷珀
  • 尽可能避免在数据库池签出期间进行冗余SELECT 1连接验证查询
  • 如果已知请求期间运行的第一个查询是幂等的,则可以
  • 直接使用它来验证连接,从而节省网络往返时间。
  • 马修·德雷珀
  • 在安全时(甚至在请求过程中)自动重新连接断开的数据库连接。
  • 当尝试运行已知幂等查询时发生错误,而
  • 不是在事务内部发生错误时,可以安全地立即重新连接到
  • 数据库服务器并重试,因此这是现在的默认行为。
  • 这个新的默认值应该始终是安全的 - 为了支持这一点,它有意识地
  • 保守哪些查询被认为是幂等的 - 但如果
  • 有必要,可以通过将connection_retries连接
  • 选项设置为 来禁用它0
  • 马修·德雷珀
  • 当存在依赖对象时,避免删除 PostgreSQL 扩展。
  • 以前,删除扩展也会隐式删除依赖对象。现在,这会引发错误。
  • 您可以强制删除扩展:
disable_extension :citext, force: :cascade
  • 修复#29091
  • 法特科迪马
  • 允许嵌套函数作为安全 SQL 字符串
  • 迈克尔·齐格弗里德
  • 允许destroy_association_async_job=使用类字符串而不是常量进行配置。
  • ActiveRecord::Base延迟和之间的自动加载依赖关系ActiveJob::Base
  • ,并将配置ActiveRecord::DestroyAssociationAsyncJob
  • 从 ActiveJob 移动到 ActiveRecord。
  • 如果作业类可卸载,或者在关联上声明了if但没有配置作业类,则弃用ActiveRecord::ActiveJobRequiredError并现在引发 a 。NameError
  • ActiveRecord::ConfigurationError
  • dependent: :destroy_async
  • 本谢尔顿
  • 修复ActiveRecord::Store序列化为常规哈希
  • 以前它会序列化为一个ActiveSupport::HashWithIndifferentAccess
  • ,这是浪费并导致 YAML safe_load 出现问题。
  • 让·布西耶
  • 添加timestamptz为 PostgreSQL 的时区感知类型
  • 这是正确解析timestamp with time zone数据库中的值所必需的。
  • 如果您不希望这样做,您可以通过添加此初始值设定项来选择退出:
ActiveRecord::Base.time_zone_aware_types -= [:timestamptz]
  • 亚历克斯·吉库列斯库
  • 添加新的ActiveRecord::Base.generates_token_forAPI。
  • 目前,signed_id履行生成令牌的作用,例如
  • 重置密码。但是,签名 ID 无法反映记录状态,因此
  • 如果令牌打算一次性使用,则必须在数据库中对其进行跟踪,至少
  • 直到其过期为止。
  • 使用generates_token_for,令牌可以嵌入记录中的数据。当
  • 使用token获取记录时,
  • 会将token中的数据与记录中的当前数据进行比较。如果两者不匹配,则令牌
  • 将被视为无效,就像过期一样。例如:
class User < ActiveRecord::Base
  has_secure_password
  generates_token_for :password_reset, expires_in: 15.minutes do
    # A password's BCrypt salt changes when the password is updated.
    # By embedding (part of) the salt in a token, the token will
    # expire when the password is updated.
    BCrypt::Password.new(password_digest).salt[-10..]
  end
end
user = User.first
token = user.generate_token_for(:password_reset)
User.find_by_token_for(:password_reset, token) # => user
user.update!(password: "new password")
User.find_by_token_for(:password_reset, token) # => nil
  • 乔纳森·赫夫纳
  • 优化整个表迭代的 Active Record 批处理。
  • 之前,in_batches获取所有 id 并IN为每个批次构建基于 的查询。
  • 当迭代整个表时,这种方法并不是最佳的,因为它会加载不需要的 id,并且
  • IN包含大量项目的查询速度很慢。
  • 现在,全表迭代id >= x AND id <= y默认使用范围迭代( ),这可以使迭代
  • 速度提高数倍。例如,在具有 1000 万条记录的 PostgreSQL 表上进行测试:查询 ( 253svs 30s)、
  • 更新 ( 288svs 124s)、删除 ( 268svs 83s)。
  • 默认情况下,只有整个表迭代使用这种迭代方式。您可以通过传递来禁用此行为use_ranges: false
  • 如果您迭代表并且唯一的条件是,例如archived_at: nil(并且只有一小部分
  • 记录被存档),那么选择这种方法是有意义的:
Project.where(archived_at: nil).in_batches(use_ranges: true) do |relation|
  # do something
end
  • 有关更多详细信息,请参阅#45414 。
  • 法特科迪马
  • .with添加了查询方法。轻松构建公用表表达式并返回ActiveRecord::Relation
Post.with(posts_with_comments: Post.where("comments_count > ?", 0))
# => ActiveRecord::Relation
# WITH posts_with_comments AS (SELECT * FROM posts WHERE (comments_count > 0)) SELECT * FROM posts
  • 弗拉多·辛格尔
  • 如果已存在相同的池,则不要建立新连接。
  • 以前,如果在establish_connection已经建立连接的类上调用,则无论配置是否相同,现有连接都将被删除。现在,如果找到与新连接具有相同值的池,则将返回现有连接而不是创建新连接。
  • 如果应用程序代码依赖于建立的新连接,无论它是否与现有连接相同,则行为会略有变化。如果需要旧行为,应用程序应ActiveRecord::Base#remove_connection在建立新行为之前调用。使用establish_connection不同的配置进行调用的方式与以前相同。
  • 艾琳·M·乌奇泰尔
  • 更新db:prepare任务以在存在未初始化的数据库时加载架构,并在迁移后转储架构。
  • 本谢尔顿
  • tsrange修复对和数组列的支持时区感知tstzrange
# In database migrations
add_column :shops, :open_hours, :tsrange, array: true
# In app config
ActiveRecord::Base.time_zone_aware_types += [:tsrange]
# In the code times are properly converted to app time zone
Shop.create!(open_hours: [Time.current..8.hour.from_now])
  • 沃伊切赫·文特尔扎克
  • 引入执行迁移的策略模式。
  • 默认情况下,迁移将使用将方法委托
  • 给连接适配器的策略对象。消费者可以实现自定义策略对象
  • 来更改其迁移的运行方式。
  • 张钧宁
  • 添加不允许外键的适配器选项
  • 这添加了一个新选项, 即使底层数据库支持外键约束,也database.yml可以跳过外键约束的使用。
  • 用法:
development:
    <<: *default
    database: storage/development.sqlite3
    foreign_keys: false
  • 保罗·巴罗斯
  • 为单一关联添加可配置的弃用警告
  • 当在 中使用单数关联的复数名称时,这会添加弃用警告where
  • 可以选择新的更高性能的行为config.active_record.allow_deprecated_singular_associations_name = false
  • 亚当·赫斯
  • 在最新实例上运行事务回调以保存
  • 事务中的给定记录。
  • 当多个 Active Record 实例更改事务中的同一条记录时
  • ,Rails仅针对 其中一个运行after_commit或回调。 添加用于指定 Rails 如何选择哪个实例接收回调 。框架默认值已更改为使用新逻辑。after_rollback
  • config.active_record.run_commit_callbacks_on_first_saved_instances_in_transaction
  • config.active_record.run_commit_callbacks_on_first_saved_instances_in_transaction
  • is 时true,事务回调将在第一个要保存的实例上运行,
  • 即使其实例状态可能已过时。
  • 当它是 时false(这是从版本 7.1 开始的新框架默认值)
  • ,事务回调将在具有最新
  • 实例状态的实例上运行。这些实例的选择如下:
  • 通常,在最后一个实例上运行事务回调以保存
  • 事务中的给定记录。
  • 有两个例外:
  • 如果记录是在事务中创建的,然后由
  • 另一个实例更新,则after_create_commit回调将在第二个实例上运行
  • 。这代替了after_update_commit
  • 根据该实例的状态天真地运行的回调。
  • 如果记录在事务中被销毁,则 即使过时的实例随后执行了更新 (这将影响 0 行),
  • after_destroy_commit也会在最后一个被销毁的实例上触发回调。
  • 卡梅伦·博特纳和米奇·沃勒布雷特
  • 为 启用严格字符串模式SQLite3Adapter
  • 使用严格字符串模式配置 SQLite,这会禁用双引号字符串文字。
  • SQLite 在双引号字符串文字方面有一些怪癖。
  • 它首先尝试将双引号字符串视为标识符名称,但如果它们不存在,
  • 则将它们视为字符串文字。正因为如此,打字错误可能会悄悄地被忽视。
  • 例如,可以为不存在的列创建索引。有关更多详细信息,
  • 请参阅SQLite 文档。
  • 如果您不希望出现此行为,可以通过以下方式禁用它:
# config/application.rb
config.active_record.sqlite3_adapter_strict_strings_by_default = false
  • 修复#27782
  • 让·布西埃·法特科迪马
  • 解决关系 cache_version 可能过时的问题。
  • 以前,当reset在关系对象上调用时,它不会重置cache_versions
  • ivar。这导致了一种令人困惑的情况,尽管拥有正确的数据,但关系
  • 仍然报告过时的缓存版本。
  • 用法:
developers = Developer.all
developers.cache_version
Developer.update_all(updated_at: Time.now.utc + 1.second)
developers.cache_version # Stale cache_version
developers.reset
developers.cache_version # Returns the current correct cache_version
  • 修复#45341
  • 奥斯汀·马登
  • 添加对排除约束的支持(仅限 PostgreSQL)。
add_exclusion_constraint :invoices, "daterange(start_date, end_date) WITH &&", using: :gist, name: "invoices_date_overlap"
remove_exclusion_constraint :invoices, name: "invoices_date_overlap"
  • CREATE TABLE ... EXCLUDE ...有关排除约束的更多信息,请参阅 PostgreSQL文档。
  • 亚历克斯·罗宾
  • change_column_null如果提供非布尔参数则引发
  • 以前,如果您提供非布尔参数,change_column_null则会
  • 将其视为真值并使您的列可为空。这可能会令人惊讶,所以现在
  • 输入必须是truefalse
change_column_null :table, :column, true # good
change_column_null :table, :column, false # good
change_column_null :table, :column, from: true, to: false # raises (previously this made the column nullable)
  • 亚历克斯·吉库列斯库
  • 对表名长度实施限制。
  • 修复#45130
  • 法特科迪马
  • 调整检查约束支持的最低 MariaDB 版本。
  • 埃迪·勒博
  • 修复 Hstore 反序列化回归。
  • 埃德夏普
  • 添加 PostgreSQL 索引的有效性。
connection.index_exists?(:users, :email, valid: true)
connection.indexes(:users).select(&:valid?)
  • 法特科迪马
  • 修复没有主键的模型的急切加载。
  • 安莫尔·乔普拉马特·劳伦斯乔纳森·海夫纳
  • 如果唯一字段未更改且由唯一索引支持,请避免验证该唯一字段。
  • 以前,在保存记录时,Active Record 将执行额外的查询来检查
  • 每个具有uniqueness验证的属性的唯一性,即使该属性尚未更改。
  • 如果数据库具有相应的唯一索引,那么对于持久记录,此验证永远不会失败
  • ,我们可以安全地跳过它。
  • 法特科迪马
  • 停止设定sql_auto_is_null
  • 从5.5版本开始默认已经关闭,我们不再需要手动关闭它。
  • 亚当·赫斯
  • 修复了touch只读列引发错误的问题。
  • 法特科迪马
  • 添加通过 SQL 架构转储的正则表达式忽略表的功能。
ActiveRecord::SchemaDumper.ignore_tables = [/^_/]
  • 法特科迪马
  • 对矛盾关系执行计算时避免查询。
  • 以前的计算即使在传递矛盾时也会进行查询
  • ,例如User.where(id: []).count。在这种情况下我们不再执行
  • 查询。
  • 这适用于以下计算:countsumaverage
  • minimummaximum
  • 卢安·维埃拉、约翰·霍索恩和丹尼尔·科尔森
  • 允许使用带有insert_all/的别名属性upsert_all
class Book < ApplicationRecord
  alias_attribute :title, :name
end
Book.insert_all [{ title: "Remote", author_id: 1 }], returning: :title
  • 法特科迪马
  • 支持具有默认数据库值的列的加密属性。
  • 这增加了对在具有默认值的列上定义的加密属性的支持。
  • 它将在创建时加密这些值。
  • 以前,除非为真,否则它会引发错误config.active_record.encryption.support_unencrypted_data
  • 豪尔赫·曼卢比亚迪马·法特科
  • 允许reading_request?覆盖DatabaseSelector::Resolver
  • 默认实现会检查请求是否为get?or head?
  • 但您现在可以将其更改为您喜欢的任何内容。如果该方法返回 true,
  • Resolver#read则被调用,这意味着该请求可以由
  • 副本数据库提供服务。
  • 亚历克斯·吉库列斯库
  • 删除ActiveRecord.legacy_connection_handling.
  • 艾琳·M·乌奇泰尔
  • rails db:schema:{dump,load}现在ENV["SCHEMA_FORMAT"]在配置之前检查
  • 自从rails db:structure:{dump,load}被弃用以来,就没有一种简单的
  • 方法可以将模式转储为 SQL 和 Ruby 格式。您现在可以使用
  • 环境变量来执行此操作。例如:
SCHEMA_FORMAT=sql rake db:schema:dump
  • 亚历克斯·吉库列斯库
  • 修复了 MariaDB 默认功能支持。
  • 默认值将在“db/schema.rb”中写入错误,并且
  • 如果使用db:schema:load. 此外,
  • 在保存新记录时,函数名称将作为字符串内容添加。
  • 卡斯佩尼
  • 添加active_record.destroy_association_async_batch_size配置
  • 这允许应用程序通过关联选项指定将在单个后台作业中销毁的最大记录数dependent: :destroy_async
  • 。默认情况下,当前行为将保持不变:
  • 当父记录被销毁时,所有依赖记录将
  • 在单个后台作业中被销毁。如果依赖记录的数量大
  • 于此配置,则记录将在多个
  • 后台作业中被销毁。
  • 尼克·霍尔登
  • 当外键实际存在时remove_foreign_key使用选项修复。:if_exists
  • 法特科迪马
  • 删除--no-commentsPostgreSQL 结构转储中的标志
  • 这破坏了一些使用自定义架构注释的应用程序。如果您不想
  • 在结构转储中添加注释,可以使用:
ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = ['--no-comments']
  • 亚历克斯·吉库列斯库
  • 减少装置访问器的内存占用。
  • 到目前为止,设备访问器都是急切地使用define_method.
  • 因此,内存使用量直接取决于装置和
  • 测试套件的数量。
  • 相反,固定装置访问器现在是用 实现的method_missing
  • 因此它们产生的内存和 CPU 开销要少得多。
  • 让·布西耶
  • 修复config.active_record.destroy_association_async_job配置
  • config.active_record.destroy_association_async_job应允许应用程序指定将用于 在后台销毁与该选项关联
  • 的关联记录的作业。以前,这一点被忽略,这 意味着默认情况下总是 在后台销毁记录。
  • has_many
  • dependent: :destroy_async
  • ActiveRecord::DestroyAssociationAsyncJob
  • 尼克·霍尔登
  • 修复change_column_comment以在 MySQL 适配器中保留列的 AUTO_INCRMENT
  • 法特科迪马
  • 修复MySQL 适配器中的引用ActiveSupport::Duration和数字。Rational
  • 凯文·麦克菲利普斯
  • 允许带有 COLLATE 的列名(例如,标题 COLLATE“C”)作为安全 SQL 字符串
  • 前田修吾
  • 允许数据库 rake 任务的 VERSION 参数中使用下划线。
  • 埃迪·勒博
  • 颠倒转储INSERT中语句的顺序structure.sql
  • 这应该会减少合并冲突的可能性。新的迁移
  • 现在将添加到列表顶部。
  • structure.sql
  • 对于现有的应用程序,下次生成时会有很大的差异。
  • 亚历克斯·吉库莱斯库马特·拉拉斯
  • 修复 ruby​​ 2.7 上的 PG.connect 关键字参数弃用警告
  • 修复#44307
  • 尼基塔·华西列夫斯基
  • 修复了序列化失败和死锁后数据库连接丢失的问题。
  • 在 6.1.4 之前,序列化失败和死锁会导致
  • 对实际事务和保存点发出回滚。这会破坏 MySQL,它
  • 不允许在死锁后回滚保存点。
  • 6.1.4 删除了事务和保存点的这些回滚,导致
  • 数据库连接处于未知状态并因此被丢弃。
  • 现在,除了 MySQL 上的保存点之外,这些回滚均已恢复。
  • 托马斯·摩根
  • 确保ActiveRecord::ConnectionPool光纤安全
  • ActiveSupport::IsolatedExecutionState.isolation_level设置为 时:fiber
  • 连接池现在支持来自同一线程的多个纤程
  • 从池中检出连接。
  • 亚历克斯·马奇尼尔
  • 添加update_attribute!ActiveRecord::Persistence
  • 与 类似update_attribute,但ActiveRecord::RecordNotSavedbefore_*回调抛出时引发:abort
class Topic < ActiveRecord::Base
  before_save :check_title
  def check_title
    throw(:abort) if title == "abort"
  end
end
topic = Topic.create(title: "Test Title")
# #=> #<Topic title: "Test Title">
topic.update_attribute!(:title, "Another Title")
# #=> #<Topic title: "Another Title">
topic.update_attribute!(:title, "abort")
# raises ActiveRecord::RecordNotSaved
  • 德鲁·坦普尔梅尔
  • 避免加载每条记录ActiveRecord::Relation#pretty_print
# Before
pp Foo.all # Loads the whole table.
# After
pp Foo.all # Shows 10 items and an ellipsis.
  • 尤利西斯·布奥诺莫
  • 更改QueryMethods#in_order_of为删除值中未列出的记录。
  • in_order_of现在过滤到提供的值,以匹配版本的行为Enumerable
  • 凯文·牛顿
  • 允许命名表达式索引可恢复。
  • 以前,由于索引删除中未使用索引名称,以下代码会在回滚时执行的可逆迁移中引发错误。
add_index(:settings, "(data->'property')", using: :gin, name: :index_settings_data_property)
  • 修复#43331
  • 奥利弗·冈瑟
  • 修复 PostgreSQL 结构转储任务中的错误参数。
  • --no-comment将Rails 7 中添加的参数更新为正确的--no-comments参数。
  • 亚历克斯·登特
  • 修复迁移兼容性,以在迁移版本为 6.0 时将 SQLite 引用/belongs_to 列创建为整数。
  • 版本 6.0 的迁移中的 Reference/belongs_to 将SQLite 适配器的列创建为bigint 而不是整数。
  • 马塞洛·劳克森
  • 修复QueryMethods#in_order_of处理空订单列表。
Post.in_order_of(:id, []).to_a
  • 还可以更明确地将列设置为二级顺序,以便任何其他
  • 值仍然有序。
  • 让·布西耶
  • 修复计算方法生成的列别名的引用。
  • 由于别名是从表名派生的,因此我们不能假设结果
  • 是有效的标识符。
class Test < ActiveRecord::Base
  self.table_name = '1abc'
end
Test.group(:id).count
# syntax error at or near "1" (ActiveRecord::StatementInvalid)
# LINE 1: SELECT COUNT(*) AS count_all, "1abc"."id" AS 1abc_id FROM "1...
  • 让·布西耶
  • authenticate_by使用时添加has_secure_password
  • authenticate_by旨在替换如下代码,
  • 当未找到具有匹配电子邮件的用户时,该代码会提前返回:
User.find_by(email: "...")&.authenticate("...")
  • 此类代码容易受到基于时间的枚举攻击,其中
  • 攻击者可以确定具有给定电子邮件的用户帐户是否存在。
  • 确认帐户存在后,攻击者可以尝试
  • 其他泄露数据库中与该电子邮件地址关联的密码,以防用户
  • 在多个站点上重复使用密码(常见做法)。此外,
  • 知道帐户电子邮件地址后,攻击者就可以尝试有针对性的网络
  • 钓鱼(“鱼叉式网络钓鱼”)攻击。
  • authenticate_by
  • 无论是否找到具有匹配电子邮件的用户,都会花费相同的时间来解决该漏洞:
User.authenticate_by(email: "...", password: "...")
  • 乔纳森·赫夫纳

动作视图

  • 介绍ActionView::TestCase.register_parser
register_parser :rss, -> rendered { RSS::Parser.parse(rendered) }<
>>
test "renders RSS" do
  article = Article.create!(title: "Hello, world")
  render formats: :rss, partial: article
  assert_equal "Hello, world", rendered.rss.items.last.title
end
  • :html默认情况下,为和注册解析器:json
  • 肖恩·多伊尔
  • 修复simple_format空白wrapper_tag选项返回纯 html 标签
  • 默认情况下,simple_format方法返回用 包裹的文本<p>。但如果我们在选项中显式指定
  • wrapper_tag: nil它会返回用标签包裹的文本<></>
  • 前:
simple_format("Hello World", {},  { wrapper_tag: nil })
# <>Hello World</>
  • 后:
simple_format("Hello World", {},  { wrapper_tag: nil })
# <p>Hello World</p>
`


© GVGNN 2013-2026