From 610147454ce14325c36c7c1b789a9d701451343d Mon Sep 17 00:00:00 2001 From: Vincent Rolea <3525369+virolea@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:17:44 +0200 Subject: [PATCH 1/5] Add new TextEntry model and updates translation model --- app/jobs/rosetta/purge_job.rb | 11 +++++++ app/models/rosetta/locale.rb | 3 +- app/models/rosetta/text_entry.rb | 22 +++++++++++++ app/models/rosetta/translation.rb | 13 ++++++-- ...40930135507_create_rosetta_text_entries.rb | 14 +++++++++ ...40930135810_update_rosetta_translations.rb | 20 ++++++++++++ test/dummy/db/schema.rb | 31 +++++++++++++------ test/fixtures/rosetta/text_entries.yml | 17 ++++++++++ test/fixtures/rosetta/translations.yml | 16 +++++----- test/models/rosetta/text_entry_test.rb | 9 ++++++ 10 files changed, 135 insertions(+), 21 deletions(-) create mode 100644 app/jobs/rosetta/purge_job.rb create mode 100644 app/models/rosetta/text_entry.rb create mode 100644 db/migrate/20240930135507_create_rosetta_text_entries.rb create mode 100644 db/migrate/20240930135810_update_rosetta_translations.rb create mode 100644 test/fixtures/rosetta/text_entries.yml create mode 100644 test/models/rosetta/text_entry_test.rb diff --git a/app/jobs/rosetta/purge_job.rb b/app/jobs/rosetta/purge_job.rb new file mode 100644 index 0000000..0cbb502 --- /dev/null +++ b/app/jobs/rosetta/purge_job.rb @@ -0,0 +1,11 @@ +module Rosetta + class PurgeJob < Rosetta::ApplicationJob + queue_as { Rosetta.config.queues[:purge] } + + discard_on ActiveRecord::RecordNotFound + + def perform(text_entry) + text_entry.purge + end + end +end diff --git a/app/models/rosetta/locale.rb b/app/models/rosetta/locale.rb index e88b416..4230273 100644 --- a/app/models/rosetta/locale.rb +++ b/app/models/rosetta/locale.rb @@ -8,8 +8,7 @@ class Locale < ApplicationRecord validates :code, uniqueness: true validates :code, format: { with: CODE_FORMAT, message: "must only contain letters separated by an optional dash" } - has_many :translations, dependent: :destroy - + has_many :text_entries, dependent: :destroy after_create_commit :notify_translated_models class << self diff --git a/app/models/rosetta/text_entry.rb b/app/models/rosetta/text_entry.rb new file mode 100644 index 0000000..5cf2813 --- /dev/null +++ b/app/models/rosetta/text_entry.rb @@ -0,0 +1,22 @@ +module Rosetta + class TextEntry < ApplicationRecord + include Translated + + belongs_to :locale + + translated_in_all_locales + + def self.create_later(content) + AutodiscoveryJob.perform_later(content) + end + + def purge + destroy + rescue ActiveRecord::InvalidForeignKey + end + + def purge_later + PurgeJob.perform_later(self) + end + end +end diff --git a/app/models/rosetta/translation.rb b/app/models/rosetta/translation.rb index a393b71..fdceb74 100644 --- a/app/models/rosetta/translation.rb +++ b/app/models/rosetta/translation.rb @@ -1,6 +1,15 @@ module Rosetta class Translation < ApplicationRecord - belongs_to :locale, class_name: "Rosetta::Locale", inverse_of: :translations - belongs_to :translation_key, class_name: "Rosetta::TranslationKey", inverse_of: :translations + belongs_to :target_locale, class_name: "Rosetta::Locale" + belongs_to :from, class_name: "Rosetta::TextEntry" + belongs_to :to, class_name: "Rosetta::TextEntry" + + after_destroy_commit :purge_orphaned_text_entries_later + + private + + def purge_orphaned_text_entries_later + to.purge_later + end end end diff --git a/db/migrate/20240930135507_create_rosetta_text_entries.rb b/db/migrate/20240930135507_create_rosetta_text_entries.rb new file mode 100644 index 0000000..31f318e --- /dev/null +++ b/db/migrate/20240930135507_create_rosetta_text_entries.rb @@ -0,0 +1,14 @@ +class CreateRosettaTextEntries < ActiveRecord::Migration[7.2] + def change + create_table :rosetta_text_entries do |t| + t.text :content, null: false + t.references :locale, null: false + + t.timestamps + + t.index :content + t.index [ :locale_id, :content ], unique: true + t.foreign_key :rosetta_locales, column: :locale_id + end + end +end diff --git a/db/migrate/20240930135810_update_rosetta_translations.rb b/db/migrate/20240930135810_update_rosetta_translations.rb new file mode 100644 index 0000000..c4dae85 --- /dev/null +++ b/db/migrate/20240930135810_update_rosetta_translations.rb @@ -0,0 +1,20 @@ +class UpdateRosettaTranslations < ActiveRecord::Migration[7.2] + def change + change_table :rosetta_translations do |t| + t.remove :value + + t.references :target_locale, null: false + t.references :from, null: false + t.references :to, null: false + + t.foreign_key :rosetta_locales, column: :target_locale_id + t.foreign_key :rosetta_text_entries, column: :from_id + t.foreign_key :rosetta_text_entries, column: :to_id + + t.index [ :target_locale_id, :from_id, :to_id ], name: :rosetta_translations_uniqueness, unique: true + end + + remove_reference :rosetta_translations, :translation_key + remove_reference :rosetta_translations, :locale + end +end diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index 0237d58..0bb311d 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2024_09_23_100651) do +ActiveRecord::Schema.define(version: 2024_09_30_135810) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -24,6 +24,16 @@ t.index ["code"], name: "index_rosetta_locales_on_code", unique: true end + create_table "rosetta_text_entries", force: :cascade do |t| + t.text "content", null: false + t.bigint "locale_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["content"], name: "index_rosetta_text_entries_on_content" + t.index ["locale_id", "content"], name: "index_rosetta_text_entries_on_locale_id_and_content", unique: true + t.index ["locale_id"], name: "index_rosetta_text_entries_on_locale_id" + end + create_table "rosetta_translation_keys", force: :cascade do |t| t.text "value" t.datetime "created_at", null: false @@ -32,16 +42,19 @@ end create_table "rosetta_translations", force: :cascade do |t| - t.text "value" - t.integer "locale_id", null: false - t.integer "translation_key_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["locale_id", "translation_key_id"], name: "index_rosetta_translations_on_locale_id_and_translation_key_id", unique: true - t.index ["locale_id"], name: "index_rosetta_translations_on_locale_id" - t.index ["translation_key_id"], name: "index_rosetta_translations_on_translation_key_id" + t.bigint "target_locale_id", null: false + t.bigint "from_id", null: false + t.bigint "to_id", null: false + t.index ["from_id"], name: "index_rosetta_translations_on_from_id" + t.index ["target_locale_id", "from_id", "to_id"], name: "rosetta_translations_uniqueness", unique: true + t.index ["target_locale_id"], name: "index_rosetta_translations_on_target_locale_id" + t.index ["to_id"], name: "index_rosetta_translations_on_to_id" end - add_foreign_key "rosetta_translations", "rosetta_locales", column: "locale_id" - add_foreign_key "rosetta_translations", "rosetta_translation_keys", column: "translation_key_id" + add_foreign_key "rosetta_text_entries", "rosetta_locales", column: "locale_id" + add_foreign_key "rosetta_translations", "rosetta_locales", column: "target_locale_id" + add_foreign_key "rosetta_translations", "rosetta_text_entries", column: "from_id" + add_foreign_key "rosetta_translations", "rosetta_text_entries", column: "to_id" end diff --git a/test/fixtures/rosetta/text_entries.yml b/test/fixtures/rosetta/text_entries.yml new file mode 100644 index 0000000..169886c --- /dev/null +++ b/test/fixtures/rosetta/text_entries.yml @@ -0,0 +1,17 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +hello: + content: hello + locale: english + +bonjour: + content: hello + locale: french + +hola: + content: hola + locale: spanish + +goodbye: + content: goodbye + locale: english diff --git a/test/fixtures/rosetta/translations.yml b/test/fixtures/rosetta/translations.yml index c0bd65c..971ced3 100644 --- a/test/fixtures/rosetta/translations.yml +++ b/test/fixtures/rosetta/translations.yml @@ -1,9 +1,9 @@ -french_hello: - translation_key: hello - locale: french - value: bonjour +hello_from_english_to_french: + from: hello + to: bonjour + locale_code: fr -spanish_hello: - translation_key: hello - locale: spanish - value: hola +hello_from_english_to_spanish: + from: hello + to: hola + locale_code: es diff --git a/test/models/rosetta/text_entry_test.rb b/test/models/rosetta/text_entry_test.rb new file mode 100644 index 0000000..c2404a2 --- /dev/null +++ b/test/models/rosetta/text_entry_test.rb @@ -0,0 +1,9 @@ +require "test_helper" + +class Rosetta::TextEntryTest < ActiveSupport::TestCase + test "purge does nothing when a translation exists" do + assert_no_difference -> { Rosetta::TextEntry.count } do + rosetta_text_entries(:hello).purge + end + end +end From 247acecd56b6a0dd8252c4920c2e2775d4926024 Mon Sep 17 00:00:00 2001 From: Vincent Rolea <3525369+virolea@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:38:39 +0200 Subject: [PATCH 2/5] Adapt code to new architecture --- .../translations/missing_controller.rb | 2 +- .../locales/translations_controller.rb | 4 +-- .../rosetta/translations_controller.rb | 14 ++++---- app/jobs/rosetta/autodiscovery_job.rb | 4 +-- .../locales/translations/_navigation.html.erb | 2 +- .../locales/translations/index.html.erb | 7 ++-- .../_text_entry_with_translation.html.erb} | 8 ++--- app/views/rosetta/translations/edit.html.erb | 8 ++--- config/routes.rb | 2 +- lib/rosetta-rails.rb | 4 +-- lib/rosetta/store.rb | 23 ++++++------- lib/rosetta/translated.rb | 27 ++++++++------- lib/rosetta/translated/create.rb | 27 ++++++++++++--- lib/rosetta/translated/delete.rb | 4 +-- .../rosetta/translations_controller_test.rb | 27 ++++----------- test/fixtures/rosetta/text_entries.yml | 2 +- test/fixtures/rosetta/translations.yml | 4 +-- test/integration/translations_test.rb | 4 +-- test/jobs/rosetta/autodiscovery_job_test.rb | 4 +-- test/lib/rosetta/translated_test.rb | 34 ++++++++++--------- test/models/rosetta/text_entry_test.rb | 2 +- test/models/rosetta/translation_test.rb | 3 +- test/rosetta_test.rb | 2 +- 23 files changed, 112 insertions(+), 106 deletions(-) rename app/views/rosetta/{locales/translations/_translation_key.html.erb => text_entries/_text_entry_with_translation.html.erb} (50%) diff --git a/app/controllers/rosetta/locales/translations/missing_controller.rb b/app/controllers/rosetta/locales/translations/missing_controller.rb index d320956..601e9fc 100644 --- a/app/controllers/rosetta/locales/translations/missing_controller.rb +++ b/app/controllers/rosetta/locales/translations/missing_controller.rb @@ -3,7 +3,7 @@ class Locales::Translations::MissingController < Locales::TranslationsController private def scope - TranslationKey.with_missing_translation(@locale) + Locale.default_locale.text_entries.missing_translation(@locale) end end end diff --git a/app/controllers/rosetta/locales/translations_controller.rb b/app/controllers/rosetta/locales/translations_controller.rb index e8e3a9a..c08d88b 100644 --- a/app/controllers/rosetta/locales/translations_controller.rb +++ b/app/controllers/rosetta/locales/translations_controller.rb @@ -3,7 +3,7 @@ class Locales::TranslationsController < ApplicationController include LocaleScoped def index - @pagy, @translation_keys = pagy(scope) + @pagy, @text_entries = pagy(scope) render "rosetta/locales/translations/index" end @@ -11,7 +11,7 @@ def index private def scope - TranslationKey.with_translation(@locale) + TextEntry.with_translated_version(@locale).where(locale: Locale.default_locale) end end end diff --git a/app/controllers/rosetta/translations_controller.rb b/app/controllers/rosetta/translations_controller.rb index 169006f..a82996a 100644 --- a/app/controllers/rosetta/translations_controller.rb +++ b/app/controllers/rosetta/translations_controller.rb @@ -2,25 +2,25 @@ module Rosetta class TranslationsController < ApplicationController include LocaleScoped - before_action :set_translation_key + before_action :set_text_entry def edit end def update - @translation_key.update(translation_key_params) + @text_entry.update(text_entry_params) - render partial: "rosetta/locales/translations/translation_key", locals: { translation_key: @translation_key } + render partial: "rosetta/text_entries/text_entry_with_translation", locals: { text_entry: @text_entry } end private - def set_translation_key - @translation_key = TranslationKey.find(params[:translation_key_id]) + def set_text_entry + @text_entry = TextEntry.find(params[:text_entry_id]) end - def translation_key_params - params.require(:translation_key).permit(:"value_#{@locale.code}") + def text_entry_params + params.require(:text_entry).permit(:"content_#{@locale.code}") end end end diff --git a/app/jobs/rosetta/autodiscovery_job.rb b/app/jobs/rosetta/autodiscovery_job.rb index df476a4..ee18d30 100644 --- a/app/jobs/rosetta/autodiscovery_job.rb +++ b/app/jobs/rosetta/autodiscovery_job.rb @@ -4,8 +4,8 @@ class AutodiscoveryJob < Rosetta::ApplicationJob discard_on ActiveRecord::RecordNotUnique - def perform(value) - TranslationKey.create!(value: value) + def perform(content) + TextEntry.create!(content: content, locale: Locale.default_locale) end end end diff --git a/app/views/rosetta/locales/translations/_navigation.html.erb b/app/views/rosetta/locales/translations/_navigation.html.erb index ce98c6b..a6d46bb 100644 --- a/app/views/rosetta/locales/translations/_navigation.html.erb +++ b/app/views/rosetta/locales/translations/_navigation.html.erb @@ -6,7 +6,7 @@ <%= tab_link_to locale_translations_missing_index_path(@locale) do %> Missing <% end %> diff --git a/app/views/rosetta/locales/translations/index.html.erb b/app/views/rosetta/locales/translations/index.html.erb index eafea57..1068686 100644 --- a/app/views/rosetta/locales/translations/index.html.erb +++ b/app/views/rosetta/locales/translations/index.html.erb @@ -33,15 +33,16 @@ - + <%= render( - collection: @translation_keys, - partial: "rosetta/locales/translations/translation_key") %> + collection: @text_entries, + as: :text_entry, + partial: "rosetta/text_entries/text_entry_with_translation") %>
Translation KeyText Entry Translation
diff --git a/app/views/rosetta/locales/translations/_translation_key.html.erb b/app/views/rosetta/text_entries/_text_entry_with_translation.html.erb similarity index 50% rename from app/views/rosetta/locales/translations/_translation_key.html.erb rename to app/views/rosetta/text_entries/_text_entry_with_translation.html.erb index c878845..5035b39 100644 --- a/app/views/rosetta/locales/translations/_translation_key.html.erb +++ b/app/views/rosetta/text_entries/_text_entry_with_translation.html.erb @@ -1,13 +1,13 @@ - <%= translation_key.value %> + <%= text_entry.content %> - <%= turbo_frame_tag dom_id(translation_key) do %> - <%= translation_key.public_send("#{@locale.code}_translation")&.value %> + <%= turbo_frame_tag dom_id(text_entry) do %> + <%= text_entry.content_in(@locale) %> <% end %> diff --git a/app/views/rosetta/translations/edit.html.erb b/app/views/rosetta/translations/edit.html.erb index 9454e6c..18a9018 100644 --- a/app/views/rosetta/translations/edit.html.erb +++ b/app/views/rosetta/translations/edit.html.erb @@ -1,10 +1,10 @@ -<%= turbo_frame_tag dom_id(@translation_key) do %> - <%= form_with model: @translation_key, url: translation_key_translation_path(@translation_key), method: :patch, class: "relative" do |f| %> +<%= turbo_frame_tag dom_id(@text_entry) do %> + <%= form_with model: @text_entry, url: text_entry_translation_path(@text_entry), method: :patch, class: "relative" do |f| %> <%= hidden_field_tag :locale_id, @locale.id %>
- <%= f.label :"value_#{@locale.code}", "Translation", class: "sr-only" %> - <%= f.text_area :"value_#{@locale.code}", row: 3, autofocus: true, placeholder: "Enter your translation", class: "block w-full resize-none border-0 bg-transparent py-1.5 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" %> + <%= f.label :"content_#{@locale.code}", "Translation", class: "sr-only" %> + <%= f.text_area :"content_#{@locale.code}", row: 3, autofocus: true, placeholder: "Enter your translation", class: "block w-full resize-none border-0 bg-transparent py-1.5 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" %>