Skip to content

Rails 8 compatibility / Version 1.4.0 - #21

Merged
tilo merged 5 commits into
mainfrom
rails-8-compatibility
Aug 23, 2026
Merged

tilo merged 5 commits into
mainfrom
rails-8-compatibility

Conversation

@tilo

@tilo tilo commented Aug 23, 2026

Copy link
Copy Markdown
Owner

No description provided.

tilo added 5 commits August 24, 2026 01:16
… and PostgreSQL

Every job of the Ruby workflow had been red since 2025-08 (tests passed, the codecov gem upload failed afterwards; JRuby cannot build the sqlite3 C extension; Ruby 2.5 has no setup-ruby build for the arm64 macos-latest runner; Rails 6.1 + concurrent-ruby >= 1.3.5 needs `require 'logger'` before `require 'active_record'`).

- ruby.yml: Ruby 2.5-4.0, head, truffleruby(-head) on ubuntu + macos (2.5 excluded on macos only); JRuby removed; new rails_matrix job: ActiveRecord 6.1, 7.0, 7.1, 7.2, 8.0, 8.1 x SQLite / PostgreSQL (postgres service container), driven by RAILS_VERSION and DB; coverage upload via codecov/codecov-action@v7 (fail_ci_if_error: false); actions/checkout@v7
- spec_helper.rb: codecov gem block removed (SimpleCov unchanged); `require 'logger'` first; DB=postgresql selects PostgreSQL
- Gemfile: https source; simplecov; pg only with DB=postgresql; activerecord pinned by RAILS_VERSION, with sqlite3 ~> 1.4 for Rails < 7.1
- gemspec: codecov removed, simplecov added, activerecord >= 6 kept as development dependency; spec.files excludes spec/ and the CI/git configuration; metadata bug_tracker_uri
- Rakefile: same structure as smarter_csv (default task spec, coverage task kept)
- .gitignore: coverage/, .rspec_status, vendor/bundle, .DS_Store
…bugs in the translation accessors

translates :name, storage: :json   # json or jsonb column (:jsonb accepted as alias)
translates :name, storage: :hstore # PostgreSQL hstore column, keys "<locale>.<attribute>"
The default (storage: :yaml, text column) is unchanged.

- Storage::Json subclasses ActiveRecord::Type::Json and symbolizes both key levels on read
- Storage::Hstore flattens / nests the translations and delegates the hstore text format to Rails' own hstore type
- Klass.translation_storage reports the storage of a model; unknown storage values raise ArgumentError
- the generated getters/setters now delegate to get_localized_attribute / set_localized_attribute

Bugs fixed (found by the new tests):
- fallbacks: reading an attribute without a translation in I18n.default_locale returned the Hash.new default value (a hash of nils) instead of nil on unsaved records
- assigning a translation equal to the current value marked the record as changed on models without a DB column for that attribute (read_attribute of a non-existent column was compared)
- set_localized_attribute compared against I18n.locale instead of its locale argument; when the current locale already held the same value, the translation for the other locale was not stored

lib/extensions/hash.rb (Hash.zip monkey patch) removed: its only caller was the Hash.new default value above.

Tests: storage_spec (one contract for yaml, json, jsonb, hstore), fallbacks_spec, storage_conversion_spec (YAML text column -> json/jsonb/hstore migration), regression tests for the three bugs. jsonb/hstore run with DB=postgresql.
README: badges as in smarter_json; Rails 6.1 / 7.x / 8.x and Ruby 2.5+ in Requirements; Example 3 (json / jsonb / hstore columns, with migrations and SQL queries); SQL queries against any locale; data migration from a YAML text column to json / jsonb / hstore; Klass.translation_storage.
…every ActiveRecord version

No change to the gem code was needed: storage: :json uses the gem's own attribute type, which reads and writes a JSON string, and MySQL's json column takes exactly that.

- spec_helper.rb / Gemfile: DB=mysql selects the mysql2 adapter (MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE)
- spec/models.rb: MySQL query for the json storage tests: JSON_UNQUOTE(JSON_EXTRACT(i18n, '$.de.name')) = ?
- ruby.yml: rails_matrix db: [sqlite3, postgresql, mysql] with a mysql:8 service container and libmysqlclient-dev for the mysql2 gem
- README / CHANGELOG: MySQL query examples; MySQL is part of the test matrix
- Klass.fallback_locales(locale): the chain configured in I18n.fallbacks (e.g. config.i18n.fallbacks = { 'de-AT' => 'de' }), then I18n.default_locale, without the locale itself. I18n.fallbacks does not append the default locale on its own, so the gem does.
- get_localized_attribute: a nil translation is looked up in the fallback locales, whatever the reason for the nil (locale absent, attribute absent, value nil). Before, only a locale without any hash fell back -- and initialize_i18n_hashes always creates an empty hash for the current I18n.locale, so a record loaded under I18n.locale = :de never fell back for :de.
- fallbacks_spec: chain cases, current-locale case, nil-value case; storage_spec: type_for_attribute('i18n').type for every storage (covers Storage::Hstore#type), unknown hstore keys are ignored
- README: fallback section describes the chain, with an example; CHANGELOG
@tilo
tilo merged commit 638608c into main Aug 23, 2026
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant