From 157a1f7690f94d7090ff364c717bbd5e43fca5a2 Mon Sep 17 00:00:00 2001 From: dafeys Date: Mon, 20 May 2024 08:31:43 +0300 Subject: [PATCH 1/6] migration: add en_name uk_name to category --- ..._and_rename_name_to_uk_name_in_categories.rb | 17 +++++++++++++++++ db/schema.rb | 8 +++++--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20240515233604_add_en_name_and_rename_name_to_uk_name_in_categories.rb diff --git a/db/migrate/20240515233604_add_en_name_and_rename_name_to_uk_name_in_categories.rb b/db/migrate/20240515233604_add_en_name_and_rename_name_to_uk_name_in_categories.rb new file mode 100644 index 000000000..cfd745f35 --- /dev/null +++ b/db/migrate/20240515233604_add_en_name_and_rename_name_to_uk_name_in_categories.rb @@ -0,0 +1,17 @@ +class AddEnNameAndRenameNameToUkNameInCategories < ActiveRecord::Migration[7.1] + def up + remove_index :categories, name: "index_categories_on_name" + rename_column :categories, :name, :uk_name + add_index :categories, "lower((uk_name)::text)", name: "index_categories_on_uk_name", unique: true + add_column :categories, :en_name, :string + add_index :categories, "lower((en_name)::text)", name: "index_categories_on_en_name", unique: true + end + + def down + remove_index :categories, name: "index_categories_on_uk_name" + rename_column :categories, :uk_name, :name + add_index :categories, "lower((name)::text)", name: "index_categories_on_name", unique: true + remove_index :categories, name: "index_categories_on_en_name" + remove_column :categories, :en_name + end +end diff --git a/db/schema.rb b/db/schema.rb index 7042f48cf..8885764a3 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_02_08_154012) do +ActiveRecord::Schema[7.1].define(version: 2024_05_15_233604) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -56,12 +56,14 @@ end create_table "categories", force: :cascade do |t| - t.string "name" + t.string "uk_name" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "priority", default: 0, null: false t.boolean "preferable", default: false, null: false - t.index "lower((name)::text)", name: "index_categories_on_name", unique: true + t.string "en_name" + t.index "lower((en_name)::text)", name: "index_categories_on_en_name", unique: true + t.index "lower((uk_name)::text)", name: "index_categories_on_uk_name", unique: true end create_table "category_categoryables", force: :cascade do |t| From 9bdb2f2dff277a878649191f89c537336720b5df Mon Sep 17 00:00:00 2001 From: dafeys Date: Mon, 20 May 2024 08:34:39 +0300 Subject: [PATCH 2/6] feature: create translatable concern --- .../account/categories_controller.rb | 7 ++++--- app/models/category.rb | 11 ++++++---- app/models/concerns/.keep | 0 app/models/concerns/translatable.rb | 21 +++++++++++++++++++ 4 files changed, 32 insertions(+), 7 deletions(-) delete mode 100644 app/models/concerns/.keep create mode 100644 app/models/concerns/translatable.rb diff --git a/app/controllers/account/categories_controller.rb b/app/controllers/account/categories_controller.rb index bd13c0bcc..7cbd6db84 100644 --- a/app/controllers/account/categories_controller.rb +++ b/app/controllers/account/categories_controller.rb @@ -4,8 +4,9 @@ class Account::CategoriesController < Account::BaseController load_and_authorize_resource def index - @q = collection.ransack(params[:q]) - @categories = @q.result.page(params[:page]) + @q = collection.ransack(params[:q]) + @categories = @q.result.page(params[:page]) + @search_attribute = :"#{I18n.locale}_name_cont" end def new @@ -56,6 +57,6 @@ def resource end def category_params - params.require(:category).permit(:name, :priority, :preferable) + params.require(:category).permit(:uk_name, :en_name, :priority, :preferable) end end diff --git a/app/models/category.rb b/app/models/category.rb index 4a8af8ca6..12fe85631 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -12,6 +12,9 @@ # preferable :boolean default: false # class Category < ApplicationRecord + include Translatable + translates :name + PRIORITY_RANGE = 0..10 enum preferable: { not_preferable: false, preferable: true } @@ -21,15 +24,15 @@ class Category < ApplicationRecord has_many :category_categoryables, dependent: :restrict_with_exception has_many :categoryables, through: :category_categoryables - validates :name, presence: true - validates :name, + validates :uk_name, :en_name, presence: true + validates :uk_name, :en_name, length: { minimum: 3, maximum: 30 }, format: { with: /\A[\p{L}0-9\s'-]+\z/i }, uniqueness: { case_sensitive: false }, allow_blank: true validates :priority, numericality: { greater_than_or_equal_to: 0 } - scope :ordered_by_name, -> { order(:name) } + scope :ordered_by_name, -> { order(:uk_name) } scope :ordered_by_priority, -> { order(:priority) } scope :unsigned_categories, ->(product) { where.not(id: product.categories_by_prices) } @@ -49,6 +52,6 @@ class Category < ApplicationRecord } def self.ransackable_attributes(auth_object = nil) - ["created_at", "id", "name", "priority", "updated_at"] + ["created_at", "id", "#{I18n.locale}_name", "priority", "updated_at"] end end diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/models/concerns/translatable.rb b/app/models/concerns/translatable.rb new file mode 100644 index 000000000..c1ef30a3d --- /dev/null +++ b/app/models/concerns/translatable.rb @@ -0,0 +1,21 @@ +module Translatable + extend ActiveSupport::Concern + + class_methods do + def translates(*attributes) + attributes.each do |attribute| + define_method(attribute) do + translation_for(attribute) + end + end + end + end + + private + + def translation_for(attribute) + public_send("#{I18n.locale}_#{attribute}") || + public_send("#{I18n.default_locale}_#{attribute}") || + public_send(attribute) + end +end From 0cc2e282affb2e804ad1433ba8922cf1bfabb78c Mon Sep 17 00:00:00 2001 From: dafeys Date: Mon, 20 May 2024 08:35:54 +0300 Subject: [PATCH 3/6] refactor: update views and translations to reflect updated category names --- app/views/account/categories/edit.html.erb | 3 ++- app/views/account/categories/index.html.erb | 2 +- app/views/account/categories/new.html.erb | 11 ++++++----- config/locales/en/en.yml | 2 ++ config/locales/uk/uk.yml | 6 +++++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/views/account/categories/edit.html.erb b/app/views/account/categories/edit.html.erb index 5f5a3f0da..54d857a93 100644 --- a/app/views/account/categories/edit.html.erb +++ b/app/views/account/categories/edit.html.erb @@ -2,7 +2,8 @@ <%= simple_form_for @category, url: { action: "update" }, html: { novalidate: false } do |f| %>
- <%= f.input :name, class: "form-control col-sm-11", required: true %> + <%= f.input :uk_name, class: "form-control col-sm-11", required: true %> + <%= f.input :en_name, class: "form-control col-sm-11", required: true %> <%= f.input :priority, collection: Category::PRIORITY_RANGE, wrapper: :custom_vertical_select %> <% if @unfilled_categories&.exclude?(@category) %>
diff --git a/app/views/account/categories/index.html.erb b/app/views/account/categories/index.html.erb index 9742a90f5..e6dbaacf1 100644 --- a/app/views/account/categories/index.html.erb +++ b/app/views/account/categories/index.html.erb @@ -5,7 +5,7 @@ <%= render partial: "account/shared/search_form", locals: { q: @q, search_url: account_categories_path, - search_attribute: :name_cont + search_attribute: @search_attribute } %>
diff --git a/app/views/account/categories/new.html.erb b/app/views/account/categories/new.html.erb index f16c39b90..f9f6156d6 100644 --- a/app/views/account/categories/new.html.erb +++ b/app/views/account/categories/new.html.erb @@ -1,14 +1,15 @@ -<%= simple_form_for @category, url: { action: 'create' }, html: { novalidate: false } do |f| %> +<%= simple_form_for @category, url: { action: "create" }, html: { novalidate: false } do |f| %>
- <%= f.input :name, class: 'form-control col-sm-11' %> + <%= f.input :uk_name, class: "form-control col-sm-11" %> + <%= f.input :en_name, class: "form-control col-sm-11" %> <%= f.input :priority, collection: Category::PRIORITY_RANGE, wrapper: :custom_vertical_select %>
- <%= f.submit t('.form.create_category_button'), class: 'btn btn-green me-2' %> - <%= link_to account_categories_path, class: 'btn btn-danger d-flex align-items-center justify-content-center' do %> - <%= t('buttons.cancel') %> + <%= f.submit t(".form.create_category_button"), class: "btn btn-green me-2" %> + <%= link_to account_categories_path, class: "btn btn-danger d-flex align-items-center justify-content-center" do %> + <%= t("buttons.cancel") %> <% end %>
<% end %> diff --git a/config/locales/en/en.yml b/config/locales/en/en.yml index 90f476e0e..49f228fbc 100644 --- a/config/locales/en/en.yml +++ b/config/locales/en/en.yml @@ -669,6 +669,8 @@ en: preferable: "Preferable" category: name: "Title" + en_name: "Title in English" + uk_name: "Title in Ukrainian" priority: "Priority" preferable: "Main" price: diff --git a/config/locales/uk/uk.yml b/config/locales/uk/uk.yml index 4354d31c7..6e7fc087c 100644 --- a/config/locales/uk/uk.yml +++ b/config/locales/uk/uk.yml @@ -546,7 +546,7 @@ uk: invalid: "містить неприпустимі символи" category: attributes: - name: + uk_name: &name_errors blank: "не може бути порожньою" too_long: one: "занадто довга (максимум %{count} знак)" @@ -560,6 +560,8 @@ uk: other: "занадто коротка (мінімум %{count} знаків)" invalid: "містить неприпустимі символи" taken: "вже викориcтовується, будь ласка, оберіть іншу" + en_name: + <<: *name_errors site_setting: attributes: title: @@ -666,6 +668,8 @@ uk: preferable: "Бажаний" category: name: "Назва" + en_name: "Назва Англійською" + uk_name: "Назва Українською" priority: "Пріоритет" preferable: "Основна" price: From d26dd5ddd7134de6551c1f062fce354aa48b616e Mon Sep 17 00:00:00 2001 From: dafeys Date: Mon, 20 May 2024 08:37:55 +0300 Subject: [PATCH 4/6] test: fix tests and add test for translatable concern --- spec/factories/categories.rb | 12 ++++-- spec/models/category_spec.rb | 15 +++++--- spec/models/conserns/translatable_spec.rb | 38 +++++++++++++++++++ spec/models/product_spec.rb | 2 +- spec/requests/categories_spec.rb | 6 +-- .../categories/preferable_service_spec.rb | 4 +- 6 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 spec/models/conserns/translatable_spec.rb diff --git a/spec/factories/categories.rb b/spec/factories/categories.rb index 6b20e3332..9148b7609 100644 --- a/spec/factories/categories.rb +++ b/spec/factories/categories.rb @@ -10,10 +10,12 @@ # FactoryBot.define do factory :category do - name { "name" } + en_name { "category" } + uk_name { "категорія" } trait :budgetary do - name { "budgetary" } + en_name { "budgetary" } + uk_name { "бюджетна" } after(:create) do |category| create(:diapers_period, category: category) @@ -21,7 +23,8 @@ end trait :medium do - name { "medium" } + en_name { "medium" } + uk_name { "середня" } preferable { true } after(:create) do |category| @@ -30,7 +33,8 @@ end trait :without_diapers_period do - name { "without-diapers-period" } + en_name { "without-diapers-period" } + uk_name { "без-diapers-period" } end end end diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index 6514a04b9..512695622 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -20,19 +20,22 @@ end describe "validations" do - it { is_expected.to validate_presence_of(:name) } - it { is_expected.to validate_length_of(:name).is_at_least(3).is_at_most(30) } - it { is_expected.to validate_uniqueness_of(:name).case_insensitive } + it { is_expected.to validate_presence_of(:en_name) } + it { is_expected.to validate_length_of(:en_name).is_at_least(3).is_at_most(30) } + it { is_expected.to validate_uniqueness_of(:en_name).case_insensitive } + it { is_expected.to validate_presence_of(:uk_name) } + it { is_expected.to validate_length_of(:uk_name).is_at_least(3).is_at_most(30) } + it { is_expected.to validate_uniqueness_of(:uk_name).case_insensitive } it { is_expected.to validate_numericality_of(:priority).is_greater_than_or_equal_to(0) } end context "checking the number of errors" do - let(:category) { build(:category, name: "") } + let(:category) { build(:category, en_name: "") } it "returns only one error message when field is blank" do category.valid? - expect(category.errors.full_messages_for(:name).length).to eq 1 - expect(category.errors.full_messages_for(:name)).to include("Title can't be blank") + expect(category.errors.full_messages_for(:en_name).length).to eq 1 + expect(category.errors.full_messages_for(:en_name)).to include("Title in English can't be blank") end end end diff --git a/spec/models/conserns/translatable_spec.rb b/spec/models/conserns/translatable_spec.rb new file mode 100644 index 000000000..f3513250f --- /dev/null +++ b/spec/models/conserns/translatable_spec.rb @@ -0,0 +1,38 @@ +require "rails_helper" + +RSpec.describe Translatable do + describe "translations" do + context "when en_name and uk_name are present" do + let(:category) { create(:category, en_name: "Books", uk_name: "Книги") } + + it "returns the en_name when locale is set to :en" do + allow(I18n).to receive(:locale).and_return(:en) + + expect(category.name).to eq("Books") + end + + it "returns the uk_name when locale is set to :uk" do + allow(I18n).to receive(:locale).and_return(:uk) + + expect(category.name).to eq("Книги") + end + end + + context "when en_name and uk_name are not present, but a default name exists" do + let(:category) { create(:category) } + + before do + allow(category).to receive(:uk_name).and_return(nil) + allow(category).to receive(:en_name).and_return(nil) + allow(category).to receive(:name).and_return("Default Name") + end + + it "returns the name when locale is set to :en" do + allow(I18n).to receive(:locale).and_return(:en) + + expect(category).not_to receive(:en_name) + expect(category.name).to eq("Default Name") + end + end + end +end diff --git a/spec/models/product_spec.rb b/spec/models/product_spec.rb index 4129b61ff..3696911ef 100644 --- a/spec/models/product_spec.rb +++ b/spec/models/product_spec.rb @@ -70,7 +70,7 @@ describe "#find_or_build_price_for_category" do let(:product) { create(:product, title: "Valid Title") } - let(:category) { create(:category, name: "Valid Name") } + let(:category) { create(:category, :medium) } let(:valid_sum) { 10.0 } context "when the product has a price for the category" do diff --git a/spec/requests/categories_spec.rb b/spec/requests/categories_spec.rb index 03456537b..8d0648d8e 100644 --- a/spec/requests/categories_spec.rb +++ b/spec/requests/categories_spec.rb @@ -2,9 +2,9 @@ RSpec.describe Account::CategoriesController, type: :request do let!(:category) { create(:category, :budgetary) } - let(:valid_attributes) { { category: { name: "medium" }} } - let(:invalid_attributes) { { category: { name: "" }} } - let(:new_attributes) { { category: { name: "premium" }} } + let(:valid_attributes) { { category: { en_name: "medium", uk_name: "середня" }} } + let(:invalid_attributes) { { category: { en_name: "" }} } + let(:new_attributes) { { category: { en_name: "premium" }} } include_context :authorize_admin diff --git a/spec/services/categories/preferable_service_spec.rb b/spec/services/categories/preferable_service_spec.rb index 6bd6ac4d3..cca69c37b 100644 --- a/spec/services/categories/preferable_service_spec.rb +++ b/spec/services/categories/preferable_service_spec.rb @@ -3,8 +3,8 @@ RSpec.describe Categories::PreferableService do describe "#call" do let!(:become_preferable_category) { create(:category, :medium) } - let!(:category) { create(:category, name: "category") } - let!(:current_preferable_category) { create(:category, name: "current preferable category", preferable: true) } + let!(:category) { create(:category, :budgetary) } + let!(:current_preferable_category) { create(:category, preferable: true) } let!(:service) { described_class.new(become_preferable_category) } context "when the category is preferable" do From 5cf8fb44431daf5979342e3c03bcebaa5e64cf1d Mon Sep 17 00:00:00 2001 From: dafeys Date: Sun, 26 May 2024 23:33:16 +0300 Subject: [PATCH 5/6] refactor: add respond_to? into traslalable --- app/models/concerns/translatable.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/concerns/translatable.rb b/app/models/concerns/translatable.rb index c1ef30a3d..126696b9c 100644 --- a/app/models/concerns/translatable.rb +++ b/app/models/concerns/translatable.rb @@ -14,8 +14,13 @@ def translates(*attributes) private def translation_for(attribute) - public_send("#{I18n.locale}_#{attribute}") || - public_send("#{I18n.default_locale}_#{attribute}") || - public_send(attribute) + locale_attr = "#{I18n.locale}_#{attribute}" + default_locale_attr = "#{I18n.default_locale}_#{attribute}" + + if respond_to?(locale_attr) + public_send(locale_attr) + elsif respond_to?(default_locale_attr) + public_send(default_locale_attr) + end end end From ccf1512f98b5f553781ba85bd2783c2b4fd153c2 Mon Sep 17 00:00:00 2001 From: dafeys Date: Sun, 26 May 2024 23:34:51 +0300 Subject: [PATCH 6/6] test: dummy model for translatable --- app/models/concerns/.keep | 0 spec/models/conserns/translatable_spec.rb | 24 +++++++++-------------- spec/support/dummy_translatable.rb | 22 +++++++++++++++++++++ 3 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 app/models/concerns/.keep create mode 100644 spec/support/dummy_translatable.rb diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/spec/models/conserns/translatable_spec.rb b/spec/models/conserns/translatable_spec.rb index f3513250f..3460bab3f 100644 --- a/spec/models/conserns/translatable_spec.rb +++ b/spec/models/conserns/translatable_spec.rb @@ -2,36 +2,30 @@ RSpec.describe Translatable do describe "translations" do - context "when en_name and uk_name are present" do - let(:category) { create(:category, en_name: "Books", uk_name: "Книги") } + context "when en_name and uk_name are exist in model" do + let(:dummy_translatable) { DummyTranslatable.new } it "returns the en_name when locale is set to :en" do allow(I18n).to receive(:locale).and_return(:en) - expect(category.name).to eq("Books") + expect(dummy_translatable.name).to eq("Diapers") end it "returns the uk_name when locale is set to :uk" do allow(I18n).to receive(:locale).and_return(:uk) - expect(category.name).to eq("Книги") + expect(dummy_translatable.name).to eq("Підгузки") end end - context "when en_name and uk_name are not present, but a default name exists" do - let(:category) { create(:category) } + context "when en_name is not exist and a default locale is uk" do + let(:dummy_translatable) { DummyTranslatable.new(en_name: nil) } - before do - allow(category).to receive(:uk_name).and_return(nil) - allow(category).to receive(:en_name).and_return(nil) - allow(category).to receive(:name).and_return("Default Name") - end - - it "returns the name when locale is set to :en" do + it "returns the uk_name when locale is set to :en" do allow(I18n).to receive(:locale).and_return(:en) + allow(I18n).to receive(:default_locale).and_return(:uk) - expect(category).not_to receive(:en_name) - expect(category.name).to eq("Default Name") + expect(dummy_translatable.name).to eq("Підгузки") end end end diff --git a/spec/support/dummy_translatable.rb b/spec/support/dummy_translatable.rb new file mode 100644 index 000000000..e96b264f4 --- /dev/null +++ b/spec/support/dummy_translatable.rb @@ -0,0 +1,22 @@ +class DummyTranslatable + include Translatable + + attr_accessor :uk_name + + translates :name + + def initialize(en_name: "Diapers", uk_name: "Підгузки") + @en_name = en_name + @uk_name = uk_name + + define_en_name if @en_name + end + + private + + # Define en_name only if en_name is present for testing case with uk defaul locale + # when en_name not exist at all + def define_en_name + define_singleton_method(:en_name) { @en_name } + end +end