{"id":15011,"date":"2026-11-02T07:00:00","date_gmt":"2026-11-02T13:00:00","guid":{"rendered":"https:\/\/www.allerin.com\/blog\/?p=15011"},"modified":"2026-10-31T07:32:17","modified_gmt":"2026-10-31T12:32:17","slug":"rails-preload-type-default-checks-30-october-2026","status":"publish","type":"post","link":"https:\/\/www.allerin.com\/blog\/rails-preload-type-default-checks-30-october-2026\/","title":{"rendered":"Rails preload, type and default checks, 30 October 2026"},"content":{"rendered":"<div class=\"allerin-rails-reading\" style=\"overflow-wrap: break-word;\">\n<p>On 2 November 2026, Rails 8.0 is five calendar days from the security-support deadline currently listed as <a href=\"https:\/\/rubyonrails.org\/maintenance\" target=\"_blank\" rel=\"noopener\">7 November<\/a>. The <a href=\"https:\/\/rubyonrails.org\/2026\/10\/30\/this-week-in-rails\" target=\"_blank\" rel=\"noopener\">30 October Rails weekly<\/a> offers useful regression checks for an upgrade: association scopes, text types, attribute defaults and generated HTML.<\/p>\n<p>These fixes are on <a href=\"https:\/\/github.com\/rails\/rails\/tree\/1248c6819cad256d3947685921cdfb5427b23e04\" target=\"_blank\" rel=\"noopener\">pinned main<\/a>, absent from the checked released tags <a href=\"https:\/\/github.com\/rails\/rails\/tree\/v8.1.4\" target=\"_blank\" rel=\"noopener\">8.1.4<\/a> and <a href=\"https:\/\/github.com\/rails\/rails\/tree\/v8.0.5.1\" target=\"_blank\" rel=\"noopener\">8.0.5.1<\/a>. They are reasons to add tests before adopting a release containing them. A main merge does not establish the release that will carry a change.<\/p>\n<p>The comparisons below ran on 31 October with Ruby 3.4.10, released Rails 8.1.4 and that exact main commit. Database examples used disposable, in-memory SQLite databases. AI assistance was used to prepare and review these synthetic fixtures. They contain no client data and establish no production-upgrade result.<\/p>\n<h2 id=\"a-preload-must-preserve-the-association-s-scope\" style=\"scroll-margin-top: 125px;\">A preload must preserve the association&#8217;s scope<\/h2>\n<p><a href=\"https:\/\/github.com\/rails\/rails\/pull\/59444\" target=\"_blank\" rel=\"noopener\">PR #59444<\/a> addresses an association whose results depend on which other association was loaded first. When an unscoped <code style=\"overflow-wrap: anywhere;\">has_many :through<\/code> association was preloaded before a scoped one sharing the same through association, Rails could reuse already loaded intermediate records and ignore the scoped association&#8217;s filtering condition.<\/p>\n<p>Preloading exists to avoid repeated association queries; it should preserve the records selected by the association. The <a href=\"https:\/\/github.com\/rails\/rails\/issues\/57861\" target=\"_blank\" rel=\"noopener\">reported example<\/a> was filed against development Rails in June. Our comparison checks released 8.1.4 as well, rather than assuming that the report&#8217;s version identifies the full affected range.<\/p>\n<p>On 8.1.4, loading all tags before inactive tags returned active tags in the inactive result. Reversing the order returned the correct identifiers. Main matched the ordinary association read in both orders, including active-only and empty controls.<\/p>\n<p>For an application using several scoped paths through one join model, compare the identifiers in both preload orders. A query-count assertion alone cannot detect the wrong records. Related fixes cover other through-preload conditions; this example does not establish every association shape&#8217;s behavior.<\/p>\n<h2 id=\"immutable-strings-should-not-erase-a-text-type\" style=\"scroll-margin-top: 125px;\">Immutable strings should not erase a text type<\/h2>\n<p>The opt-in <code style=\"overflow-wrap: anywhere;\">immutable_strings_by_default<\/code> setting arrived in <a href=\"https:\/\/github.com\/rails\/rails\/pull\/39599\" target=\"_blank\" rel=\"noopener\">Rails 6.1<\/a> after its merge in 2020. It lets applications that do not mutate returned strings avoid the duplication associated with mutation detection. It defaults to false; installing a newer Rails version does not by itself enable it.<\/p>\n<p><a href=\"https:\/\/github.com\/rails\/rails\/pull\/59351\" target=\"_blank\" rel=\"noopener\">PR #59351<\/a> narrows that setting to actual string columns. Previously, types inheriting string behavior could also be converted. The upstream patch identifies text and several PostgreSQL types whose type information or specialized behavior could be lost.<\/p>\n<p>In our SQLite fixture, enabling the setting made a text column report <code style=\"overflow-wrap: anywhere;\">:string<\/code> and return a frozen value on Rails 8.1.4. On main, it remained <code style=\"overflow-wrap: anywhere;\">:text<\/code> and returned a mutable value. The ordinary string column remained frozen on both. Disabling the setting preserved the text type on both. These results cover SQLite string and text columns; the PostgreSQL consequences come from source review, not a PostgreSQL execution here.<\/p>\n<p>Inspect explicit uses of this setting and assert both the reported type and the mutation behavior the application expects. Do not enable it merely to obtain a new default, or infer that a successful value round trip proves that type-specific behavior survived.<\/p>\n<h2 id=\"check-a-declared-default-after-reset\" style=\"scroll-margin-top: 125px;\">Check a declared default after reset<\/h2>\n<p>CurrentAttributes shipped in <a href=\"https:\/\/github.com\/rails\/rails\/blob\/v5.2.0\/guides\/source\/5_2_release_notes.md\" target=\"_blank\" rel=\"noopener\">Rails 5.2 in 2018<\/a>. Its separate <code style=\"overflow-wrap: anywhere;\">default:<\/code> option arrived in <a href=\"https:\/\/github.com\/rails\/rails\/blob\/v7.2.0\/activesupport\/CHANGELOG.md\" target=\"_blank\" rel=\"noopener\">Rails 7.2 in 2024<\/a>. This week&#8217;s correction concerns attributes declared with string names.<\/p>\n<p><a href=\"https:\/\/github.com\/rails\/rails\/pull\/59347\" target=\"_blank\" rel=\"noopener\">PR #59347<\/a> normalizes CurrentAttributes names to symbols when registering defaults. A string-named attribute could have a generated reader but a default stored under the wrong key.<\/p>\n<p>The synthetic declaration <code style=\"overflow-wrap: anywhere;\">attribute \"region\", default: \"Europe\"<\/code> returned <code style=\"overflow-wrap: anywhere;\">nil<\/code> on first access and after reset in Rails 8.1.4. Main returned <code style=\"overflow-wrap: anywhere;\">\"Europe\"<\/code> in both cases. A symbol-named control worked on both, including restoration of a fresh mutable default after changing the previous value.<\/p>\n<p>For code declaring these attributes dynamically, test the initial read and the reset path. Explicitly assigning a value can hide the defect. This check concerns registration and reset of a synthetic value; it does not test request isolation or concurrency.<\/p>\n<h2 id=\"inspect-the-html-that-a-helper-emits\" style=\"scroll-margin-top: 125px;\">Inspect the HTML that a helper emits<\/h2>\n<p><a href=\"https:\/\/github.com\/rails\/rails\/blob\/v5.2.0\/actionview\/lib\/action_view\/helpers\/tag_helper.rb\" target=\"_blank\" rel=\"noopener\">Rails 5.2&#8217;s tag helper<\/a>, released in 2018, already treated <code style=\"overflow-wrap: anywhere;\">hidden<\/code> as a boolean attribute. That treatment persists in 8.1.4. <a href=\"https:\/\/github.com\/rails\/rails\/pull\/59340\" target=\"_blank\" rel=\"noopener\">PR #59340<\/a> preserves string and symbol values for <code style=\"overflow-wrap: anywhere;\">hidden<\/code>, including <code style=\"overflow-wrap: anywhere;\">until-found<\/code>.<\/p>\n<p>This complete example ran against both checked sources:<\/p>\n<pre style=\"max-width: 100%; overflow-x: auto; white-space: pre-wrap; overflow-wrap: anywhere;\"><code class=\"language-ruby\">require \"action_view\"\r\nhelper = Object.new.extend(ActionView::Helpers::TagHelper)\r\nputs helper.tag.div(hidden: \"until-found\")\r\n<\/code><\/pre>\n<p>Rails 8.1.4 emitted <code style=\"overflow-wrap: anywhere;\">&lt;div hidden=\"hidden\"&gt;&lt;\/div&gt;<\/code>; main emitted <code style=\"overflow-wrap: anywhere;\">&lt;div hidden=\"until-found\"&gt;&lt;\/div&gt;<\/code>. Boolean true still emitted <code style=\"overflow-wrap: anywhere;\">hidden=\"hidden\"<\/code>, and false omitted the attribute, on both. The fixture verifies HTML serialization, not browser reveal behavior. If a view relies on the keyword, inspect its rendered attribute and test the intended interaction separately.<\/p>\n<h2 id=\"what-to-carry-into-the-upgrade\" style=\"scroll-margin-top: 125px;\">What to carry into the upgrade<\/h2>\n<ul>\n<li>Compare scoped association identifiers before and after preloading, in both relevant orders.<\/li>\n<li>Check explicit immutable-string settings against text and specialized column types.<\/li>\n<li>Exercise CurrentAttributes defaults both initially and after reset.<\/li>\n<li>Assert keyword-valued HTML attributes in rendered views.<\/li>\n<\/ul>\n<p>Use the <a href=\"https:\/\/www.allerin.com\/services\/rails-upgrades\">Rails upgrade review<\/a> to connect those checks to the application&#8217;s next version step. Recheck the <a href=\"https:\/\/www.allerin.com\/blog\/rails-support-calendar-explained\/\">support calendar<\/a> and actual released patches before choosing a deployment target.<\/p>\n<h2 id=\"sources\" style=\"scroll-margin-top: 125px;\">Sources<\/h2>\n<ul>\n<li><a href=\"https:\/\/rubyonrails.org\/2026\/10\/30\/this-week-in-rails\" target=\"_blank\" rel=\"noopener\">Official weekly and linked changes<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/rails\/rails\/pull\/59444\" target=\"_blank\" rel=\"noopener\">Scoped through-preload fix<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/rails\/rails\/pull\/59351\" target=\"_blank\" rel=\"noopener\">Immutable-string type correction<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/rails\/rails\/pull\/59347\" target=\"_blank\" rel=\"noopener\">CurrentAttributes defaults correction<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/rails\/rails\/pull\/59340\" target=\"_blank\" rel=\"noopener\">Hidden attribute keyword handling<\/a><\/li>\n<li><a href=\"https:\/\/guides.rubyonrails.org\/maintenance_policy.html#release-schedule\" target=\"_blank\" rel=\"noopener\">Maintenance policy and release-schedule extension rule<\/a><\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>The October 30 Rails changes suggest four regression checks: scoped preload results, retained text types, CurrentAttributes defaults and keyword-valued HTML. Synthetic fixtures compare Rails 8.1.4 with pinned main.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[2037],"tags":[2041,2090,2044,2061,2038],"class_list":["post-15011","post","type-post","status-publish","format-standard","hentry","category-ruby-on-rails","tag-active-record","tag-currentattributes","tag-rails-maintenance","tag-rails-testing","tag-rails-upgrades"],"_links":{"self":[{"href":"https:\/\/www.allerin.com\/blog\/wp-json\/wp\/v2\/posts\/15011","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.allerin.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.allerin.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.allerin.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.allerin.com\/blog\/wp-json\/wp\/v2\/comments?post=15011"}],"version-history":[{"count":2,"href":"https:\/\/www.allerin.com\/blog\/wp-json\/wp\/v2\/posts\/15011\/revisions"}],"predecessor-version":[{"id":15013,"href":"https:\/\/www.allerin.com\/blog\/wp-json\/wp\/v2\/posts\/15011\/revisions\/15013"}],"wp:attachment":[{"href":"https:\/\/www.allerin.com\/blog\/wp-json\/wp\/v2\/media?parent=15011"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.allerin.com\/blog\/wp-json\/wp\/v2\/categories?post=15011"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.allerin.com\/blog\/wp-json\/wp\/v2\/tags?post=15011"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}