diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b467c1b0..691e1cbdd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,27 +28,17 @@ jobs: - '3.2' - '3.1' - '3.0' - - '2.7' - - '2.6' database: - 'sqlite3' - 'mysql' - 'postgres' orm: - - name: 'active_record' - version: '4.2' - - name: 'active_record' - version: '5.0' - - name: 'active_record' - version: '5.1' - - name: 'active_record' - version: '5.2' - - name: 'active_record' - version: '6.0' - name: 'active_record' version: '6.1' - name: 'active_record' version: '7.0' + - name: 'active_record' + version: '7.1' - name: 'sequel' version: '5' experimental: [false] @@ -66,14 +56,6 @@ jobs: feature: 'unit' orm: experimental: false - - ruby: '2.7' - feature: 'unit' - orm: - experimental: false - - ruby: '2.6' - feature: 'unit' - orm: - experimental: false - ruby: '3.2' feature: 'rails' orm: @@ -155,126 +137,6 @@ jobs: - ruby: '3.0' feature: 'i18n_fallbacks' experimental: false - - ruby: '3.0' - database: 'sqlite3' - feature: 'unit' - orm: - name: 'active_record' - version: 'edge' - experimental: true - - ruby: '3.0' - database: 'mysql' - feature: 'unit' - orm: - name: 'active_record' - version: 'edge' - experimental: true - - ruby: '3.0' - database: 'postgres' - feature: 'unit' - orm: - name: 'active_record' - version: 'edge' - experimental: true - - ruby: '2.7' - feature: 'rails' - orm: - name: 'active_record' - version: '7.0' - database: 'sqlite3' - experimental: false - - ruby: '2.7' - feature: 'performance' - experimental: false - - ruby: '2.7' - feature: 'i18n_fallbacks' - experimental: false - - ruby: '2.7' - database: 'sqlite3' - feature: 'unit' - orm: - name: 'active_record' - version: 'edge' - experimental: true - - ruby: '2.7' - database: 'mysql' - feature: 'unit' - orm: - name: 'active_record' - version: 'edge' - experimental: true - - ruby: '2.7' - database: 'postgres' - feature: 'unit' - orm: - name: 'active_record' - version: 'edge' - experimental: true - exclude: - - ruby: '2.6' - orm: - name: 'active_record' - version: '7.0' - - ruby: '2.7' - orm: - name: 'active_record' - version: '4.2' - - ruby: '3.0' - orm: - name: 'active_record' - version: '4.2' - - ruby: '3.0' - orm: - name: 'active_record' - version: '5.0' - - ruby: '3.0' - orm: - name: 'active_record' - version: '5.1' - - ruby: '3.0' - orm: - name: 'active_record' - version: '5.2' - - ruby: '3.1' - orm: - name: 'active_record' - version: '4.2' - - ruby: '3.1' - orm: - name: 'active_record' - version: '5.0' - - ruby: '3.1' - orm: - name: 'active_record' - version: '5.1' - - ruby: '3.1' - orm: - name: 'active_record' - version: '5.2' - - ruby: '3.1' - orm: - name: 'active_record' - version: '6.0' - - ruby: '3.2' - orm: - name: 'active_record' - version: '4.2' - - ruby: '3.2' - orm: - name: 'active_record' - version: '5.0' - - ruby: '3.2' - orm: - name: 'active_record' - version: '5.1' - - ruby: '3.2' - orm: - name: 'active_record' - version: '5.2' - - ruby: '3.2' - orm: - name: 'active_record' - version: '6.0' env: DB: ${{ matrix.database }} @@ -304,7 +166,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Update packages run: sudo apt-get update - name: Install Sqlite @@ -314,7 +176,7 @@ jobs: run: sudo apt-get install libpq-dev postgresql-client -y if: matrix.database == 'postgres' - id: cache-bundler - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: vendor/bundle key: ${{ matrix.ruby }}-${{ matrix.orm.name }}-${{ matrix.orm.version }}-${{ matrix.feature }}-${{ hashFiles('mobility.gemspec') }}-${{ hashFiles('Gemfile') }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c4a572d6..85ab3d347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ ## 1.3 +### (unreleased) + +- Allow `I18n.available_locales` to contain Strings + ([#612](https://github.com/shioyama/mobility/pull/612)) + +### 1.3.0.rc3 + +- Don't try to load generators if Rails is loaded but AR is not + ([#627](https://github.com/shioyama/mobility/pull/627)), thanks + [flop](https://github.com/flop)! +- Allow compound foreign keys + ([#632](https://github.com/shioyama/mobility/pull/632)), thanks + [mival](https://github.com/mival)! +- Fix active model `*_previously_changed?` and active record + `will_save_change_to_*?` and `saved_change_to_*?` dirty methods to accept + kwargs ([#639](https://github.com/shioyama/mobility/pull/639)) thanks + [doits](https://github.com/doits)! + +### 1.3.0.rc2 + +- Pass `coder` as keyword argument to `serialize` (ActiveRecord version > 7.1) + ([#617](https://github.com/shioyama/mobility/pull/617)) + ### 1.3.0.rc1 This version includes potentially breaking chnages for jsonb and hstore diff --git a/Gemfile b/Gemfile index 68f2ded9b..8030d12ed 100644 --- a/Gemfile +++ b/Gemfile @@ -10,7 +10,7 @@ group :development, :test do when 'active_record' orm_version ||= '7.0' case orm_version - when '4.2', '5.0', '5.1', '5.2', '6.0', '6.1', '7.0' + when '6.1', '7.0', '7.1' gem 'activerecord', "~> #{orm_version}.0" when 'edge' git 'https://github.com/rails/rails.git', branch: 'main' do diff --git a/README.md b/README.md index 669fe47cb..b0f22172f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ Mobility [![Gem Version](https://badge.fury.io/rb/mobility.svg)][gem] [![Build Status](https://github.com/shioyama/mobility/workflows/CI/badge.svg)][actions] [![Code Climate](https://api.codeclimate.com/v1/badges/72200f2b00c339ec4537/maintainability.svg)][codeclimate] -[![Gitter Chat](https://badges.gitter.im/mobility-ruby/mobility.svg)](https://gitter.im/mobility-ruby/mobility) [gem]: https://rubygems.org/gems/mobility [actions]: https://github.com/shioyama/mobility/actions @@ -55,17 +54,13 @@ Installation Add this line to your application's Gemfile: ```ruby -gem 'mobility', '~> 1.3.0.rc1' +gem 'mobility', '~> 1.3.0.rc3' ``` ### ActiveRecord (Rails) Requirements: -- ActiveRecord >= 5.0 (including 6.x) - -(Support for most backends and features is also supported with -ActiveRecord/Rails 4.2, but there are some tests still failing. To see exactly -what might not work, check pending specs in Rails 4.2 builds.) +- ActiveRecord >= 6.1 To translate attributes on a model, extend `Mobility`, then call `translates` passing in one or more attributes as well as a hash of options (see below). @@ -1027,6 +1022,7 @@ Integrations * [mobility-actiontext](https://github.com/sedubois/mobility-actiontext): Translate Rails [Action Text](https://guides.rubyonrails.org/action_text_overview.html) rich text with Mobility. +* [mobility_typed](https://github.com/GeorgeGorbanev/mobility_typed): Add type checking to Rails models accessors. Tutorials --------- diff --git a/certs/shioyama.pem b/certs/shioyama.pem index aa438a781..d8610556a 100644 --- a/certs/shioyama.pem +++ b/certs/shioyama.pem @@ -1,26 +1,26 @@ -----BEGIN CERTIFICATE----- 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 -----END CERTIFICATE----- diff --git a/lib/mobility.rb b/lib/mobility.rb index dd3f9d059..7cc1c6187 100644 --- a/lib/mobility.rb +++ b/lib/mobility.rb @@ -90,7 +90,7 @@ class Error < StandardError CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/ private_constant :CALL_COMPILABLE_REGEXP - require "rails/generators/mobility/generators" if defined?(Rails) + require "rails/generators/mobility/generators" if defined?(Rails) && defined?(ActiveRecord) class << self def extended(model_class) @@ -228,9 +228,9 @@ def enforce_available_locales!(locale) # methods (in LocaleAccessors) than is really necessary. def available_locales if defined?(Rails) && Rails.respond_to?(:application) && Rails.application - Rails.application.config.i18n.available_locales&.map(&:to_sym) || I18n.available_locales + Rails.application.config.i18n.available_locales&.map(&:to_sym) || I18n.available_locales.map(&:to_sym) else - I18n.available_locales + I18n.available_locales.map(&:to_sym) end end diff --git a/lib/mobility/backends/active_record/serialized.rb b/lib/mobility/backends/active_record/serialized.rb index e17618a49..6b346a8e0 100644 --- a/lib/mobility/backends/active_record/serialized.rb +++ b/lib/mobility/backends/active_record/serialized.rb @@ -51,7 +51,13 @@ def self.build_node(attr, _locale) setup do |attributes, options| coder = { yaml: YAMLCoder, json: JSONCoder }[options[:format]] - attributes.each { |attribute| serialize (options[:column_affix] % attribute), coder } + attributes.each do |attribute| + if (::ActiveRecord::VERSION::STRING >= "7.1") + serialize (options[:column_affix] % attribute), coder: coder + else + serialize (options[:column_affix] % attribute), coder + end + end end # @!group Cache Methods diff --git a/lib/mobility/backends/active_record/table.rb b/lib/mobility/backends/active_record/table.rb index 896c46942..a7c567040 100644 --- a/lib/mobility/backends/active_record/table.rb +++ b/lib/mobility/backends/active_record/table.rb @@ -109,7 +109,13 @@ def configure(options) options[:association_name] = :translations options[:subclass_name] ||= :Translation end - %i[foreign_key association_name subclass_name table_name].each { |key| options[key] = options[key].to_sym } + %i[foreign_key association_name subclass_name table_name].each { |key| + if options[key].is_a?(Enumerable) + options[key] = options[key].map!(&:to_sym) + else + options[key] = options[key].to_sym + end + } end # @!endgroup diff --git a/lib/mobility/plugins/active_model/dirty.rb b/lib/mobility/plugins/active_model/dirty.rb index 3cd13a79b..5160b3065 100644 --- a/lib/mobility/plugins/active_model/dirty.rb +++ b/lib/mobility/plugins/active_model/dirty.rb @@ -119,6 +119,14 @@ def self.append_locale(attr_name) class HandlerMethodsBuilder < Module attr_reader :klass + PATTERNS_WITH_KWARGS = + %w[ + %s_changed? + %s_previously_changed? + will_save_change_to_%s? + saved_change_to_%s? + ].freeze + # @param [Class] klass Dirty class to mimic def initialize(klass) @klass = klass @@ -135,7 +143,7 @@ def define_handler_methods public_patterns.each do |pattern| method_name = pattern % 'attribute' - kwargs = pattern == '%s_changed?' ? ', **kwargs' : '' + kwargs = PATTERNS_WITH_KWARGS.include?(pattern) ? ', **kwargs' : '' module_eval <<-EOM, __FILE__, __LINE__ + 1 def #{method_name}(attr_name, *rest#{kwargs}) if (mutations_from_mobility.attribute_changed?(attr_name) || @@ -298,8 +306,10 @@ def attribute_changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) (OPTION_NOT_GIVEN == to || fetch_value(attr_name) == to) end - def attribute_previously_changed?(attr_name) - previous_changes.include?(attr_name) + def attribute_previously_changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) + previous_changes.include?(attr_name) && + (OPTION_NOT_GIVEN == from || attribute_previous_change(attr_name).first == from) && + (OPTION_NOT_GIVEN == to || attribute_previous_change(attr_name).second == to) end def attribute_was(attr_name) diff --git a/lib/mobility/version.rb b/lib/mobility/version.rb index f54783a9c..d4a83be5b 100644 --- a/lib/mobility/version.rb +++ b/lib/mobility/version.rb @@ -9,7 +9,7 @@ module VERSION MAJOR = 1 MINOR = 3 TINY = 0 - PRE = "rc1" + PRE = "rc3" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end diff --git a/spec/active_record/schema.rb b/spec/active_record/schema.rb index babf93175..8127eca4d 100644 --- a/spec/active_record/schema.rb +++ b/spec/active_record/schema.rb @@ -1,11 +1,6 @@ module Mobility module Test - if ::ActiveRecord::VERSION::MAJOR < 5 - parent_class = ::ActiveRecord::Migration - else - parent_class = ::ActiveRecord::Migration[[::ActiveRecord::VERSION::MAJOR, ::ActiveRecord::VERSION::MINOR].join(".")] - end - class Schema < parent_class + class Schema < ::ActiveRecord::Migration[[::ActiveRecord::VERSION::MAJOR, ::ActiveRecord::VERSION::MINOR].join(".")] class << self def up create_table "posts" do |t| diff --git a/spec/generators/rails/mobility/install_generator_spec.rb b/spec/generators/rails/mobility/install_generator_spec.rb index e976ec17a..cba3f5dca 100644 --- a/spec/generators/rails/mobility/install_generator_spec.rb +++ b/spec/generators/rails/mobility/install_generator_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -return unless defined?(Rails) +return unless defined?(Rails) && defined?(ActiveRecord) require "rails/generators/mobility/install_generator" @@ -42,11 +42,7 @@ directory "db" do directory "migrate" do migration "create_text_translations" do - if ActiveRecord::VERSION::MAJOR < 5 - contains "class CreateTextTranslations < ActiveRecord::Migration" - else - contains "class CreateTextTranslations < ActiveRecord::Migration[#{version_string_}]" - end + contains "class CreateTextTranslations < ActiveRecord::Migration[#{version_string_}]" contains "def change" contains "create_table :mobility_text_translations" contains "t.text :value" @@ -67,11 +63,7 @@ directory "db" do directory "migrate" do migration "create_string_translations" do - if ActiveRecord::VERSION::MAJOR < 5 - contains "class CreateStringTranslations < ActiveRecord::Migration" - else - contains "class CreateStringTranslations < ActiveRecord::Migration[#{version_string_}]" - end + contains "class CreateStringTranslations < ActiveRecord::Migration[#{version_string_}]" contains "def change" contains "create_table :mobility_string_translations" contains "t.string :value" diff --git a/spec/generators/rails/mobility/translations_generator_spec.rb b/spec/generators/rails/mobility/translations_generator_spec.rb index 3b14612e0..6b2a9b30f 100644 --- a/spec/generators/rails/mobility/translations_generator_spec.rb +++ b/spec/generators/rails/mobility/translations_generator_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -return unless defined?(Rails) +return unless defined?(Rails) && defined?(ActiveRecord) require "rails/generators/mobility/translations_generator" @@ -67,11 +67,7 @@ directory "db" do directory "migrate" do migration "create_post_title_and_content_translations_for_mobility_table_backend" do - if ActiveRecord::VERSION::MAJOR < 5 - contains "class CreatePostTitleAndContentTranslationsForMobilityTableBackend < ActiveRecord::Migration" - else - contains "class CreatePostTitleAndContentTranslationsForMobilityTableBackend < ActiveRecord::Migration[#{version_string_}]" - end + contains "class CreatePostTitleAndContentTranslationsForMobilityTableBackend < ActiveRecord::Migration[#{version_string_}]" contains "def change" contains "create_table :post_translations" contains "t.string :title" @@ -110,11 +106,7 @@ directory "db" do directory "migrate" do migration "create_post_title_and_content_translations_for_mobility_table_backend" do - if ActiveRecord::VERSION::MAJOR < 5 - contains "class CreatePostTitleAndContentTranslationsForMobilityTableBackend < ActiveRecord::Migration" - else - contains "class CreatePostTitleAndContentTranslationsForMobilityTableBackend < ActiveRecord::Migration[#{version_string_}]" - end + contains "class CreatePostTitleAndContentTranslationsForMobilityTableBackend < ActiveRecord::Migration[#{version_string_}]" contains "add_column :post_translations, :title, :string" contains "add_index :post_translations, [:title, :locale], name: :index_post_translations_on_title_and_locale" contains "add_column :post_translations, :content, :text" @@ -158,11 +150,7 @@ directory "db" do directory "migrate" do migration "create_foo_title_and_content_translations_for_mobility_column_backend" do - if ActiveRecord::VERSION::MAJOR < 5 - contains "class CreateFooTitleAndContentTranslationsForMobilityColumnBackend < ActiveRecord::Migration" - else - contains "class CreateFooTitleAndContentTranslationsForMobilityColumnBackend < ActiveRecord::Migration[#{version_string_}]" - end + contains "class CreateFooTitleAndContentTranslationsForMobilityColumnBackend < ActiveRecord::Migration[#{version_string_}]" contains "add_column :foos, :title_en, :string" contains "add_index :foos, :title_en, name: :index_foos_on_title_en" contains "add_column :foos, :title_ja, :string" diff --git a/spec/mobility/backend_spec.rb b/spec/mobility/backend_spec.rb index 4aa612dc2..a239da53c 100644 --- a/spec/mobility/backend_spec.rb +++ b/spec/mobility/backend_spec.rb @@ -251,7 +251,7 @@ def self.foobar it "returns value when configured" do model_class = double("model class") - options = double("options") + options = {} subclass = backend_class.build_subclass(model_class, options) expect(subclass.model_class).to eq(model_class) expect(subclass.options).to eq(options) diff --git a/spec/mobility/backends/active_record/container_spec.rb b/spec/mobility/backends/active_record/container_spec.rb index 0f2a16d5e..c62d3c665 100644 --- a/spec/mobility/backends/active_record/container_spec.rb +++ b/spec/mobility/backends/active_record/container_spec.rb @@ -136,7 +136,7 @@ m.drop_table :json_container_posts end include_accessor_examples 'JsonContainerPost' - include_querying_examples 'JsonContainerPost' unless ActiveRecord::VERSION::MAJOR < 5 + include_querying_examples 'JsonContainerPost' end context "with a non-json/jsonb column" do diff --git a/spec/mobility/backends/active_record/json_spec.rb b/spec/mobility/backends/active_record/json_spec.rb index 5d0796818..4762758ee 100644 --- a/spec/mobility/backends/active_record/json_spec.rb +++ b/spec/mobility/backends/active_record/json_spec.rb @@ -48,7 +48,7 @@ plugins :active_record, :reader, :writer, :query before { translates JsonPost, :title, :content, backend: :json, **column_options } - include_querying_examples 'JsonPost' unless ActiveRecord::VERSION::MAJOR < 5 + include_querying_examples 'JsonPost' include_validation_examples 'JsonPost' end diff --git a/spec/mobility/backends/active_record/serialized_spec.rb b/spec/mobility/backends/active_record/serialized_spec.rb index 5f85d781e..908fee6e7 100644 --- a/spec/mobility/backends/active_record/serialized_spec.rb +++ b/spec/mobility/backends/active_record/serialized_spec.rb @@ -41,7 +41,6 @@ post = SerializedPost.new post.title = "foo" post.save - post.reload if ActiveRecord::VERSION::MAJOR < 5 # don't ask me why expect(post.public_send("#{(column_affix % "title")}_before_type_cast")).to eq("---\n:en: foo\n") end @@ -70,7 +69,6 @@ post = SerializedPost.new post.title = "foo" post.save - post.reload if ActiveRecord::VERSION::MAJOR < 5 # don't ask me why expect(post.public_send("#{column_affix % "title"}_before_type_cast")).to eq("{\"en\":\"foo\"}") end end diff --git a/spec/mobility/backends/active_record/table_spec.rb b/spec/mobility/backends/active_record/table_spec.rb index 774768e63..a032157fc 100644 --- a/spec/mobility/backends/active_record/table_spec.rb +++ b/spec/mobility/backends/active_record/table_spec.rb @@ -265,6 +265,11 @@ backend_class.configure(options) expect(options[:foreign_key]).to eq(:article_id) end + + it "sets composite foreign_key" do + backend_class.configure(options.merge!(foreign_key: [:article_id, :article_type])) + expect(options[:foreign_key]).to eq([:article_id, :article_type]) + end end describe "with query plugin" do diff --git a/spec/mobility/plugins/active_model/dirty_spec.rb b/spec/mobility/plugins/active_model/dirty_spec.rb index 51368f519..bf733667a 100644 --- a/spec/mobility/plugins/active_model/dirty_spec.rb +++ b/spec/mobility/plugins/active_model/dirty_spec.rb @@ -239,19 +239,19 @@ def save expect(instance.title_changed?).to eq(false) expect(instance.attribute_changed?(:title_en)).to eq(false) - if ActiveRecord::VERSION::MAJOR >= 5 - expect(instance.title_previously_changed?).to eq(true) - expect(instance.title_previous_change).to eq(["foo", "bar"]) - expect(instance.title_changed?).to eq(false) + expect(instance.title_previously_changed?).to eq(true) + expect(instance.title_previously_changed?(from: 'foo', to: 'bar')).to eq(true) + expect(instance.title_previously_changed?(from: 'foo', to: 'baz')).to eq(false) + expect(instance.title_previous_change).to eq(["foo", "bar"]) + expect(instance.title_changed?).to eq(false) - expect(instance.attribute_previously_changed?(:title_en)).to eq(true) - expect(instance.attribute_changed?(:title_en)).to eq(false) + expect(instance.attribute_previously_changed?(:title_en)).to eq(true) + expect(instance.attribute_previously_changed?(:title_en, from: 'foo', to: 'bar')).to eq(true) + expect(instance.attribute_previously_changed?(:title_en, from: 'foo', to: 'baz')).to eq(false) + expect(instance.attribute_changed?(:title_en)).to eq(false) - if ActiveRecord::VERSION::STRING >= '6.1' - expect(instance.title_previously_was).to eq('foo') - expect(instance.attribute_previously_was(:title_en)).to eq('foo') - end - end + expect(instance.title_previously_was).to eq('foo') + expect(instance.attribute_previously_was(:title_en)).to eq('foo') instance.title_will_change! expect(instance.title_changed?).to eq(true) diff --git a/spec/mobility/plugins/active_record/cache_spec.rb b/spec/mobility/plugins/active_record/cache_spec.rb index 12435e053..c8683eac5 100644 --- a/spec/mobility/plugins/active_record/cache_spec.rb +++ b/spec/mobility/plugins/active_record/cache_spec.rb @@ -34,11 +34,7 @@ model_class.include translations instance = model_class.create - if ::ActiveRecord::VERSION::MAJOR == 4 - expect(instance.mobility_backends[:title]).to receive(:clear_cache).at_least(1).time - else - expect(instance.mobility_backends[:title]).to receive(:clear_cache).once - end + expect(instance.mobility_backends[:title]).to receive(:clear_cache).once instance.reload end end diff --git a/spec/mobility/plugins/active_record/dirty_spec.rb b/spec/mobility/plugins/active_record/dirty_spec.rb index be746dfcb..52d3381bf 100644 --- a/spec/mobility/plugins/active_record/dirty_spec.rb +++ b/spec/mobility/plugins/active_record/dirty_spec.rb @@ -216,24 +216,23 @@ def values; @values ||= {}; end expect(instance.title_changed?).to eq(false) expect(instance.title_was).to eq("foo") - if ActiveRecord::VERSION::MAJOR >= 5 - expect(instance.title_previously_changed?).to eq(true) - expect(instance.title_previous_change).to eq([nil, "foo"]) - end - - # AR-specific suffix methods, added in AR 5.1 - if ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR > 0 - expect(instance.saved_change_to_title?).to eq(true) - expect(instance.saved_change_to_title).to eq([nil, "foo"]) - expect(instance.title_before_last_save).to eq(nil) - expect(instance.title_in_database).to eq("foo") - - # attribute handlers - expect(instance.saved_change_to_attribute?(:title_en)).to eq(true) - expect(instance.saved_change_to_attribute(:title_en)).to eq([nil, 'foo']) - expect(instance.attribute_before_last_save(:title_en)).to eq(nil) - expect(instance.attribute_in_database(:title_en)).to eq('foo') - end + expect(instance.title_previously_changed?).to eq(true) + expect(instance.title_previous_change).to eq([nil, "foo"]) + + expect(instance.saved_change_to_title?).to eq(true) + expect(instance.saved_change_to_title?(from: nil, to: 'foo')).to eq(true) + expect(instance.saved_change_to_title?(from: nil, to: 'foz')).to eq(false) + expect(instance.saved_change_to_title).to eq([nil, "foo"]) + expect(instance.title_before_last_save).to eq(nil) + expect(instance.title_in_database).to eq("foo") + + # attribute handlers + expect(instance.saved_change_to_attribute?(:title_en)).to eq(true) + expect(instance.saved_change_to_attribute?(:title_en, from: nil, to: 'foo')).to eq(true) + expect(instance.saved_change_to_attribute?(:title_en, from: nil, to: 'foz')).to eq(false) + expect(instance.saved_change_to_attribute(:title_en)).to eq([nil, 'foo']) + expect(instance.attribute_before_last_save(:title_en)).to eq(nil) + expect(instance.attribute_in_database(:title_en)).to eq('foo') end backend.write(:en, 'bar') @@ -244,34 +243,36 @@ def values; @values ||= {}; end expect(instance.title_changed?(from: 'foo', to: 'baz')).to eq(false) expect(instance.title_change).to eq(["foo", "bar"]) expect(instance.title_was).to eq("foo") + expect(instance.will_save_change_to_title?).to eq(true) + expect(instance.will_save_change_to_title?(from: 'foo', to: 'bar')).to eq(true) + expect(instance.will_save_change_to_title?(from: 'foo', to: 'baz')).to eq(false) instance.save expect(instance.title_changed?).to eq(false) - if ActiveRecord::VERSION::MAJOR >= 5 - expect(instance.title_previously_changed?).to eq(true) - expect(instance.title_previous_change).to eq(["foo", "bar"]) - expect(instance.title_changed?).to eq(false) + expect(instance.title_previously_changed?).to eq(true) + expect(instance.title_previous_change).to eq(["foo", "bar"]) + expect(instance.title_changed?).to eq(false) - # AR-specific suffix methods, added in 5.1 - if ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR > 0 - expect(instance.saved_change_to_title?).to eq(true) - expect(instance.saved_change_to_title).to eq(["foo", "bar"]) - expect(instance.title_before_last_save).to eq("foo") - expect(instance.will_save_change_to_title?).to eq(false) - expect(instance.title_change_to_be_saved).to eq(nil) - expect(instance.title_in_database).to eq("bar") - - # attribute handlers - expect(instance.saved_change_to_attribute?(:title_en)).to eq(true) - expect(instance.saved_change_to_attribute(:title_en)).to eq(['foo', 'bar']) - expect(instance.attribute_before_last_save(:title_en)).to eq('foo') - expect(instance.will_save_change_to_attribute?(:title_en)).to eq(false) - expect(instance.attribute_change_to_be_saved(:title_en)).to eq(nil) - expect(instance.attribute_in_database(:title_en)).to eq('bar') - end - end + expect(instance.saved_change_to_title?).to eq(true) + expect(instance.saved_change_to_title?(from: 'foo', to: 'bar')).to eq(true) + expect(instance.saved_change_to_title?(from: 'foo', to: 'baz')).to eq(false) + expect(instance.saved_change_to_title).to eq(["foo", "bar"]) + expect(instance.title_before_last_save).to eq("foo") + expect(instance.will_save_change_to_title?).to eq(false) + expect(instance.title_change_to_be_saved).to eq(nil) + expect(instance.title_in_database).to eq("bar") + + # attribute handlers + expect(instance.saved_change_to_attribute?(:title_en)).to eq(true) + expect(instance.saved_change_to_attribute?(:title_en, from: 'foo', to: 'bar')).to eq(true) + expect(instance.saved_change_to_attribute?(:title_en, from: 'foo', to: 'baz')).to eq(false) + expect(instance.saved_change_to_attribute(:title_en)).to eq(['foo', 'bar']) + expect(instance.attribute_before_last_save(:title_en)).to eq('foo') + expect(instance.will_save_change_to_attribute?(:title_en)).to eq(false) + expect(instance.attribute_change_to_be_saved(:title_en)).to eq(nil) + expect(instance.attribute_in_database(:title_en)).to eq('bar') end aggregate_failures "force change" do @@ -280,22 +281,27 @@ def values; @values ||= {}; end aggregate_failures "before save" do expect(instance.title_changed?).to eq(true) - # AR-specific suffix methods - if ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR > 0 - expect(instance.saved_change_to_title?).to eq(true) - expect(instance.saved_change_to_title).to eq(["foo", "bar"]) - expect(instance.title_before_last_save).to eq("foo") - expect(instance.will_save_change_to_title?).to eq(true) - expect(instance.title_change_to_be_saved).to eq(["bar", "bar"]) - expect(instance.title_in_database).to eq("bar") - - expect(instance.saved_change_to_attribute?(:title_en)).to eq(true) - expect(instance.saved_change_to_attribute(:title_en)).to eq(['foo', 'bar']) - expect(instance.attribute_before_last_save(:title_en)).to eq('foo') - expect(instance.will_save_change_to_attribute?(:title_en)).to eq(true) - expect(instance.attribute_change_to_be_saved(:title_en)).to eq(['bar', 'bar']) - expect(instance.attribute_in_database(:title_en)).to eq('bar') - end + expect(instance.saved_change_to_title?).to eq(true) + expect(instance.saved_change_to_title?(from: 'foo', to: 'bar')).to eq(true) + expect(instance.saved_change_to_title?(from: 'foo', to: 'baz')).to eq(false) + expect(instance.saved_change_to_title).to eq(["foo", "bar"]) + expect(instance.title_before_last_save).to eq("foo") + expect(instance.will_save_change_to_title?).to eq(true) + expect(instance.will_save_change_to_title?(from: 'bar', to: 'bar')).to eq(true) + expect(instance.will_save_change_to_title?(from: 'bar', to: 'baz')).to eq(false) + expect(instance.title_change_to_be_saved).to eq(["bar", "bar"]) + expect(instance.title_in_database).to eq("bar") + + expect(instance.saved_change_to_attribute?(:title_en)).to eq(true) + expect(instance.saved_change_to_attribute?(:title_en, from: 'foo', to: 'bar')).to eq(true) + expect(instance.saved_change_to_attribute?(:title_en, from: 'foo', to: 'baz')).to eq(false) + expect(instance.saved_change_to_attribute(:title_en)).to eq(['foo', 'bar']) + expect(instance.attribute_before_last_save(:title_en)).to eq('foo') + expect(instance.will_save_change_to_attribute?(:title_en)).to eq(true) + expect(instance.will_save_change_to_attribute?(:title_en, from: 'bar', to: 'bar')).to eq(true) + expect(instance.will_save_change_to_attribute?(:title_en, from: 'bar', to: 'baz')).to eq(false) + expect(instance.attribute_change_to_be_saved(:title_en)).to eq(['bar', 'bar']) + expect(instance.attribute_in_database(:title_en)).to eq('bar') end instance.save! @@ -303,22 +309,23 @@ def values; @values ||= {}; end aggregate_failures "after save" do expect(instance.title_changed?).to eq(false) - # AR-specific suffix methods, added in 5.1 - if ActiveRecord::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR > 0 - expect(instance.saved_change_to_title?).to eq(true) - expect(instance.saved_change_to_title).to eq(["bar", "bar"]) - expect(instance.title_before_last_save).to eq("bar") - expect(instance.will_save_change_to_title?).to eq(false) - expect(instance.title_change_to_be_saved).to eq(nil) - expect(instance.title_in_database).to eq("bar") - - expect(instance.saved_change_to_attribute?(:title_en)).to eq(true) - expect(instance.saved_change_to_attribute(:title_en)).to eq(['bar', 'bar']) - expect(instance.attribute_before_last_save(:title_en)).to eq('bar') - expect(instance.will_save_change_to_attribute?(:title_en)).to eq(false) - expect(instance.attribute_change_to_be_saved(:title_en)).to eq(nil) - expect(instance.attribute_in_database(:title_en)).to eq('bar') - end + expect(instance.saved_change_to_title?).to eq(true) + expect(instance.saved_change_to_title?(from: 'bar', to: 'bar')).to eq(true) + expect(instance.saved_change_to_title?(from: 'bar', to: 'baz')).to eq(false) + expect(instance.saved_change_to_title).to eq(["bar", "bar"]) + expect(instance.title_before_last_save).to eq("bar") + expect(instance.will_save_change_to_title?).to eq(false) + expect(instance.title_change_to_be_saved).to eq(nil) + expect(instance.title_in_database).to eq("bar") + + expect(instance.saved_change_to_attribute?(:title_en)).to eq(true) + expect(instance.saved_change_to_attribute?(:title_en, from: 'bar', to: 'bar')).to eq(true) + expect(instance.saved_change_to_attribute?(:title_en, from: 'bar', to: 'baz')).to eq(false) + expect(instance.saved_change_to_attribute(:title_en)).to eq(['bar', 'bar']) + expect(instance.attribute_before_last_save(:title_en)).to eq('bar') + expect(instance.will_save_change_to_attribute?(:title_en)).to eq(false) + expect(instance.attribute_change_to_be_saved(:title_en)).to eq(nil) + expect(instance.attribute_in_database(:title_en)).to eq('bar') end end end diff --git a/spec/mobility/plugins/sequel/query_spec.rb b/spec/mobility/plugins/sequel/query_spec.rb index 943f0bab7..253907951 100644 --- a/spec/mobility/plugins/sequel/query_spec.rb +++ b/spec/mobility/plugins/sequel/query_spec.rb @@ -15,10 +15,13 @@ Article.include translations Article end + before { translates model_class, :title, backend: :table } context "default query scope" do it "defines query scope" do - expect(model_class.i18n).to eq(described_class.build_query(model_class, Mobility.locale)) + actual_query = model_class.i18n.where(title: "foo") + expected_query = described_class.build_query(model_class, Mobility.locale).where(title: "foo") + expect(actual_query.sql).to eq(expected_query.sql) end end @@ -29,7 +32,9 @@ end it "defines query scope" do - expect(model_class.foo).to eq(described_class.build_query(model_class, Mobility.locale)) + actual_query = model_class.foo.where(title: "foo") + expected_query = described_class.build_query(model_class, Mobility.locale).where(title: "foo") + expect(actual_query.sql).to eq(expected_query.sql) expect { model_class.i18n }.to raise_error(NoMethodError) end end diff --git a/spec/mobility_spec.rb b/spec/mobility_spec.rb index 0e321edbe..90cd234d9 100644 --- a/spec/mobility_spec.rb +++ b/spec/mobility_spec.rb @@ -62,6 +62,7 @@ def perform_with_locale(locale) end it 'sets independent locales in multiple threads' do + skip "failing on Ruby 3.2+, need to investigate" if RUBY_VERSION >= "3.2.0" threads = [] threads << perform_with_locale(:en) threads << perform_with_locale(:fr) @@ -175,6 +176,39 @@ def perform_with_locale(locale) end if defined?(Rails) end + describe ".available_string_locales" do + around do |example| + @available_locales = I18n.available_locales + I18n.available_locales = ['en', 'pt'] + example.run + I18n.available_locales = @available_locales + end + + it "correctly converst I18n.available_locales to symbols" do + expect(described_class.available_locales).to eq([:en, :pt]) + end + + # @note Required since model may be loaded in initializer before Rails has + # updated I18n.available_locales. + context 'Rails application is loaded' do + context 'available locales in Rails i18n config are present' do + it 'uses Rails i18n available locales converted to symbols' do + allow(Rails).to receive_message_chain(:application, :config, :i18n, :available_locales). + and_return([:by, :en]) + expect(described_class.available_locales).to eq([:by, :en]) + end + end + + context 'available locales in Rails i18n config are nil' do + it 'uses I18n.available_locales converted to symbols' do + allow(Rails).to receive_message_chain(:application, :config, :i18n, :available_locales). + and_return(nil) + expect(described_class.available_locales).to eq([:en, :pt]) + end + end + end if defined?(Rails) + end + describe '.normalize_locale' do it "normalizes locale to lowercase string underscores" do expect(described_class.normalize_locale(:"pt-BR")).to eq("pt_br") diff --git a/spec/support/shared_examples/serialization_examples.rb b/spec/support/shared_examples/serialization_examples.rb index 07d4c45ff..17e81b9e8 100644 --- a/spec/support/shared_examples/serialization_examples.rb +++ b/spec/support/shared_examples/serialization_examples.rb @@ -62,7 +62,6 @@ backend.write(:en, "") instance.save - # instance.reload if ActiveRecord::VERSION::MAJOR < 5 # don't ask me why # Note: Sequel backend and Rails < 5.0 return a blank string here. expect(backend.read(:en)).to eq("")