このコミットが含まれているのは:
2026-10-17 16:47:08 +09:00
コミット b945ceef60
91個のファイルの変更、4652行の追加、822行の削除
+1 -11
ファイルの表示
@@ -24,17 +24,7 @@ FactoryBot.define do
evaluator.primary_tag_name.update!(tag:, primary_flg: true)
Locale.where(deprecated_at: nil).order(:language_code, :created_at, :script_code).to_a
.uniq(&:language_code).each do |locale|
next if tag.tag_names.exists?(language_code: locale.language_code,
primary_flg: true)
create(:tag_name,
tag:, language_code: locale.language_code,
script_code: locale.script_code,
name: TagName.generate_name(locale, tag, evaluator.primary_tag_name),
primary_flg: true, auto_generated: true)
end
TagNameCompletion.for_tag!(tag)
end
end
end
+2 -3
ファイルの表示
@@ -6,9 +6,8 @@ RSpec.describe Language, type: :model do
expect(described_class.where(deprecated_at: nil).pluck(:code)).to eq(['ja'])
end
it 'accepts English when multilingual support is enabled' do
pending '日本語以外の Language 有効化は別課題で実装する'
expect { described_class.create!(code: 'en', name: 'English') }
it 'accepts Ainu when multilingual support is enabled' do
expect { described_class.create!(code: 'ain', name: 'ain') }
.to change(described_class, :count).by(1)
end
+82
ファイルの表示
@@ -0,0 +1,82 @@
require 'rails_helper'
RSpec.describe 'Locale activation', type: :model do
let!(:language) { Language.create!(code: 'ain', name: 'ain') }
let!(:script) { Script.create!(code: 'Kana', name: 'Kana') }
let!(:first) { create(:tag, primary_name: 'activation_first') }
let!(:second) { create(:tag, primary_name: 'activation_second') }
def build_locale deprecated_at: nil
Locale.new(code: 'ain-Kana', language_code: language.code,
script_code: script.code, region_code: 392, variants: '',
name: 'ain-Kana', deprecated_at:)
end
def generated_names
TagName.where(language_code: 'ain', primary_flg: true)
end
def fail_second_generation!
allow(TagName).to receive(:generate_name).and_call_original
allow(TagName).to receive(:generate_name)
.with(kind_of(Locale), second, second.tag_name('ja'))
.and_raise('backfill failed')
end
it 'rolls back a newly created locale and the preceding generated names' do
locale = build_locale
fail_second_generation!
expect { locale.save! }.to raise_error('backfill failed')
expect(Locale.exists?(code: 'ain-Kana')).to be(false)
expect(generated_names).to be_empty
expect(first.reload.name('ja')).to eq('activation_first')
expect(second.reload.name('ja')).to eq('activation_second')
end
it 'does not backfill while the locale is inactive' do
build_locale(deprecated_at: Time.current).save!
expect(generated_names).to be_empty
end
it 'backfills missing primary names when the locale becomes active' do
locale = build_locale(deprecated_at: Time.current)
locale.save!
locale.update!(deprecated_at: nil)
expect(generated_names.pluck(:tag_id)).to contain_exactly(first.id, second.id)
expect(generated_names).to all(have_attributes(
script_code: 'Kana', primary_flg: true, auto_generated: true))
end
it 'rolls back activation and all generated names when backfill fails' do
locale = build_locale(deprecated_at: Time.current)
locale.save!
previous_deprecated_at = locale.reload.deprecated_at
fail_second_generation!
expect { locale.update!(deprecated_at: nil) }.to raise_error('backfill failed')
expect(locale.reload.deprecated_at).to eq(previous_deprecated_at)
expect(generated_names).to be_empty
end
it 'preserves a human primary through deactivation and repeated activation' do
locale = build_locale
locale.save!
primary = generated_names.find_by!(tag: first)
primary.update!(name: 'human_confirmed', auto_generated: false)
snapshot = generated_names.order(:id).map(&:attributes)
2.times do
locale.update!(deprecated_at: Time.current)
locale.update!(deprecated_at: nil)
end
expect(generated_names.order(:id).map(&:attributes)).to eq(snapshot)
expect(primary.reload).to have_attributes(name: 'human_confirmed', auto_generated: false)
end
end
+42 -50
ファイルの表示
@@ -1,13 +1,13 @@
require 'rails_helper'
RSpec.describe Locale, type: :model do
include_context 'English locale'
include_context 'Ainu locale'
def create_english_locale!
prepare_english_reference!
described_class.create!(code: 'en', language_code: 'en',
script_code: 'Latn', region_code: 826,
variants: '', name: 'English')
def create_ainu_locale!
prepare_ainu_reference!
described_class.create!(code: 'ain-Kana', language_code: 'ain',
script_code: 'Kana', region_code: 392,
variants: '', name: 'Ainu')
end
it 'keeps the active Nipponese locale consistent with its identity tuple' do
@@ -27,7 +27,6 @@ RSpec.describe Locale, type: :model do
end
it 'generates a primary name for every existing tag using the name generator' do
pending '日本語以外の Locale 有効化は別課題で実装する'
first = create(:tag, primary_name: 'first_existing_tag')
second = create(:tag, primary_name: 'second_existing_tag')
allow(TagName).to receive(:generate_name).and_call_original
@@ -35,63 +34,60 @@ RSpec.describe Locale, type: :model do
.with(kind_of(described_class), first, primary_tag_name_for(first, 'ja'))
.and_return('name_from_generator')
locale = create_english_locale!
locale = create_ainu_locale!
expect(TagName).to have_received(:generate_name)
.with(locale, first, primary_tag_name_for(first, 'ja'))
expect(TagName).to have_received(:generate_name)
.with(locale, second, primary_tag_name_for(second, 'ja'))
expect(first.tag_names.find_by!(language_code: 'en', primary_flg: true))
expect(first.tag_names.find_by!(language_code: 'ain', primary_flg: true))
.to have_attributes(name: 'name_from_generator', tag_id: first.id,
language_code: 'en', script_code: 'Latn',
language_code: 'ain', script_code: 'Kana',
primary_flg: true, auto_generated: true)
expect(second.tag_names.find_by!(language_code: 'en', primary_flg: true))
.to have_attributes(tag_id: second.id, language_code: 'en',
script_code: 'Latn', primary_flg: true,
expect(second.tag_names.find_by!(language_code: 'ain', primary_flg: true))
.to have_attributes(tag_id: second.id, language_code: 'ain',
script_code: 'Kana', primary_flg: true,
auto_generated: true)
expect(TagName.where(language_code: 'en', primary_flg: true,
expect(TagName.where(language_code: 'ain', primary_flg: true,
tag_id: [first.id, second.id]).count).to eq(2)
end
it 'does not duplicate an existing primary name in the new language' do
pending '日本語以外の Locale 有効化は別課題で実装する'
prepare_english_reference!
prepare_ainu_reference!
tag = create(:tag, primary_name: 'already_named')
existing = create(:tag_name, name: 'existing_english', tag:,
language_code: 'en', script_code: 'Latn')
existing = create(:tag_name, name: 'existing_ainu', tag:,
language_code: 'ain', script_code: 'Kana')
create_english_locale!
create_ainu_locale!
expect(TagName.where(tag_id: tag.id, language_code: 'en', primary_flg: true))
expect(TagName.where(tag_id: tag.id, language_code: 'ain', primary_flg: true))
.to contain_exactly(existing)
expect(existing.reload).to have_attributes(
tag_id: tag.id, language_code: 'en', script_code: 'Latn',
tag_id: tag.id, language_code: 'ain', script_code: 'Kana',
primary_flg: true, auto_generated: false)
end
it 'creates a primary name when the language has only an alias' do
pending '日本語以外の Locale 有効化は別課題で実装する'
prepare_english_reference!
prepare_ainu_reference!
tag = create(:tag, primary_name: 'alias_only_tag')
alias_name = create(:tag_name, :alias, name: 'alias_en',
tag:,
language_code: 'en', script_code: 'Latn')
language_code: 'ain', script_code: 'Kana')
create_english_locale!
create_ainu_locale!
expect(alias_name.reload).to have_attributes(
tag_id: tag.id,
language_code: 'en', script_code: 'Latn', primary_flg: false)
generated = TagName.find_by!(tag_id: tag.id, language_code: 'en', primary_flg: true)
expect(generated).to have_attributes(tag_id: tag.id, language_code: 'en',
script_code: 'Latn', primary_flg: true,
language_code: 'ain', script_code: 'Kana', primary_flg: false)
generated = TagName.find_by!(tag_id: tag.id, language_code: 'ain', primary_flg: true)
expect(generated).to have_attributes(tag_id: tag.id, language_code: 'ain',
script_code: 'Kana', primary_flg: true,
auto_generated: true)
expect(TagName.where(tag_id: tag.id, language_code: 'en', primary_flg: true).count).to eq(1)
expect(TagName.where(tag_id: tag.id, language_code: 'ain', primary_flg: true).count).to eq(1)
end
it 'rolls back the locale and all generated names when backfill fails' do
pending '日本語以外の Locale 有効化は別課題で実装する'
prepare_english_reference!
prepare_ainu_reference!
first = create(:tag, primary_name: 'atomic_first')
second = create(:tag, primary_name: 'atomic_second')
allow(TagName).to receive(:generate_name).and_call_original
@@ -99,31 +95,27 @@ RSpec.describe Locale, type: :model do
.with(kind_of(described_class), second, primary_tag_name_for(second, 'ja'))
.and_raise('backfill failed')
expect { create_english_locale! }.to raise_error('backfill failed')
expect { create_ainu_locale! }.to raise_error('backfill failed')
expect(described_class.exists?(code: 'en')).to be(false)
expect(TagName.where(language_code: 'en', tag_id: [first.id, second.id])).to be_empty
expect(described_class.exists?(code: 'ain-Kana')).to be(false)
expect(TagName.where(language_code: 'ain', tag_id: [first.id, second.id])).to be_empty
end
it 'accepts a stable locale code outside the historical enum values' do
pending '日本語以外の Locale 有効化は別課題で実装する'
prepare_english_reference!
it 'rejects a private locale code outside the supported locale set' do
prepare_ainu_reference!
locale = described_class.create!(
code: 'en-x-rspec', language_code: 'en',
script_code: 'Latn', region_code: 826,
variants: 'x-rspec', name: 'English')
expect(locale.reload).to have_attributes(
code: 'en-x-rspec', language_code: 'en', script_code: 'Latn',
region_code: 826, variants: 'x-rspec')
expect {
described_class.create!(
code: 'ain-Kana-x-rspec', language_code: 'ain',
script_code: 'Kana', region_code: 392,
variants: 'x-rspec', name: 'Ainu')
}.to raise_error(ArgumentError)
end
it 'registers the future British English locale tuple' do
pending '日本語以外の Locale 有効化は別課題で実装する'
it 'registers the supported Ainu locale tuple' do
expect(locale).to have_attributes(code: 'en', language_code: 'en',
script_code: 'Latn', region_code: 826,
expect(locale).to have_attributes(code: 'ain-Kana', language_code: 'ain',
script_code: 'Kana', region_code: 392,
variants: '')
end
end
+3 -4
ファイルの表示
@@ -1,7 +1,7 @@
require 'rails_helper'
RSpec.describe Script, type: :model do
include_context 'English locale'
include_context 'Ainu locale'
it 'selects locales by script code, not language code' do
expect(described_class.find('Jpan').locales).to contain_exactly(Locale.nipponese)
@@ -25,11 +25,10 @@ RSpec.describe Script, type: :model do
}
end
it 'selects Latin locales when multilingual support is enabled' do
pending '日本語以外の Locale 有効化は別課題で実装する'
it 'selects Kana locales when multilingual support is enabled' do
locale
expect(described_class.find('Latn').locales).to contain_exactly(locale)
expect(described_class.find('Kana').locales).to contain_exactly(locale)
expect(described_class.find('Jpan').locales).not_to include(locale)
end
end
+12 -14
ファイルの表示
@@ -95,38 +95,36 @@ RSpec.describe TagNameSanitisationRule, type: :model do
end
context 'when only another language has the sanitised name' do
include_context 'English locale'
include_context 'Ainu locale'
it 'keeps both names when sanitisation produces a cross-language match' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
nipponese = create(:tag_name, name: 'foobar')
english = create(:tag_name, name: 'temporary',
language_code: 'en', script_code: 'Latn')
english.update_columns(name: 'foo_bar')
ainu = create(:tag_name, name: 'temporary',
language_code: 'ain', script_code: 'Kana')
ainu.update_columns(name: 'foo_bar')
expect { described_class.apply! }.not_to change(TagName, :count)
expect(english.reload).to have_attributes(name: 'foobar', language_code: 'en')
expect(ainu.reload).to have_attributes(name: 'foobar', language_code: 'ain')
expect(nipponese.reload).to have_attributes(name: 'foobar', language_code: 'ja')
end
it 'renames without merging tags or deleting either language identity' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
nipponese = create(:tag, primary_name: 'foobar')
english_name = create(:tag_name, name: 'temporary',
language_code: 'en', script_code: 'Latn')
english = create(:tag, primary_tag_name: english_name)
english_name.update_columns(name: 'foo_bar')
ainu_name = create(:tag_name, name: 'temporary',
language_code: 'ain', script_code: 'Kana')
ainu = create(:tag, primary_tag_name: ainu_name)
ainu_name.update_columns(name: 'foo_bar')
described_class.apply!
expect(english_name.reload).to have_attributes(
name: 'foobar', language_code: 'en', tag_id: english.id)
expect(ainu_name.reload).to have_attributes(
name: 'foobar', language_code: 'ain', tag_id: ainu.id)
expect(primary_tag_name_for(nipponese.reload, 'ja')).to have_attributes(
name: 'foobar', language_code: 'ja', tag_id: nipponese.id)
expect(primary_tag_name_for(english.reload, 'en')).to eq(english_name)
expect(primary_tag_name_for(ainu.reload, 'ain')).to eq(ainu_name)
end
end
+25 -29
ファイルの表示
@@ -1,17 +1,16 @@
require 'rails_helper'
RSpec.describe TagName, type: :model do
include_context 'English locale'
include_context 'Ainu locale'
it 'allows the same name in different languages' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
create(:tag_name, name: 'shared_name')
english = build(:tag_name, name: 'shared_name',
language_code: 'en', script_code: 'Latn')
ainu = build(:tag_name, name: 'shared_name',
language_code: 'ain', script_code: 'Kana')
expect(english).to be_valid
expect { english.save! }.to change(described_class, :count).by(1)
expect(ainu).to be_valid
expect { ainu.save! }.to change(described_class, :count).by(1)
end
it 'rejects a duplicate name within the same language' do
@@ -67,11 +66,10 @@ RSpec.describe TagName, type: :model do
end
it 'completes an ordinary tag factory under future active languages' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
tag = create(:tag, primary_name: 'future_factory')
expect(TagName.where(tag:, language_code: 'en', primary_flg: true))
expect(TagName.where(tag:, language_code: 'ain', primary_flg: true))
.to contain_exactly(have_attributes(auto_generated: true))
end
@@ -114,46 +112,44 @@ RSpec.describe TagName, type: :model do
end
it 'resolves the primary name of the alias language on the same tag' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
tag = create(:tag, primary_name: '日本語正本')
english_primary = primary_tag_name_for(tag, 'en')
english_primary.update!(name: 'english_primary', auto_generated: false)
english_alias = create(:tag_name, :alias, tag:, name: 'english_alias',
language_code: 'en', script_code: 'Latn')
ainu_primary = primary_tag_name_for(tag, 'ain')
ainu_primary.update!(name: 'ainu_primary', auto_generated: false)
ainu_alias = create(:tag_name, :alias, tag:, name: 'ainu_alias',
language_code: 'ain', script_code: 'Kana')
representative_id = tag.tag_name('ja')&.id
primary = described_class.find_by!(tag_id: english_alias.tag_id,
language_code: english_alias.language_code,
primary = described_class.find_by!(tag_id: ainu_alias.tag_id,
language_code: ainu_alias.language_code,
primary_flg: true)
expect(primary).to eq(english_primary)
expect(described_class.canonicalise(locale, [english_alias.name]))
.to eq([english_primary.name])
expect(primary).to eq(ainu_primary)
expect(described_class.canonicalise(locale, [ainu_alias.name]))
.to eq([ainu_primary.name])
expect(tag.reload.tag_name('ja')&.id).to eq(representative_id)
end
it 'resolves only aliases in the requested language' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
nipponese = create(:tag, primary_name: 'nipponese_canonical')
english_name = create(:tag_name, name: 'english_canonical',
language_code: 'en', script_code: 'Latn')
english = create(:tag, primary_tag_name: english_name)
ainu_name = create(:tag_name, name: 'ainu_canonical',
language_code: 'ain', script_code: 'Kana')
ainu = create(:tag, primary_tag_name: ainu_name)
nipponese_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: nipponese)
english_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: english,
language_code: 'en', script_code: 'Latn')
ainu_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: ainu,
language_code: 'ain', script_code: 'Kana')
expect(nipponese_alias.reload).to have_attributes(
tag_id: nipponese.id,
primary_flg: false, language_code: 'ja')
expect(english_alias.reload).to have_attributes(
tag_id: english.id,
primary_flg: false, language_code: 'en')
expect(ainu_alias.reload).to have_attributes(
tag_id: ainu.id,
primary_flg: false, language_code: 'ain')
expect(described_class.canonicalise(locale, ['shared_alias']))
.to eq(['english_canonical'])
.to eq(['ainu_canonical'])
expect(described_class.canonicalise(Locale.nipponese, ['shared_alias']))
.to eq(['nipponese_canonical'])
end
+63 -74
ファイルの表示
@@ -8,7 +8,7 @@ RSpec.describe Tag, type: :model do
it 'checks the requested language and treats an empty description as present' do
TagDescription.create!(tag:, language_code: 'ja', body: '', version_no: 1)
expect(tag.has_description?(language_code: 'ja')).to be(true)
expect(tag.has_description?(language_code: 'en')).to be(false)
expect(tag.has_description?(language_code: 'ain')).to be(false)
end
it 'returns false without a description' do
@@ -23,16 +23,16 @@ RSpec.describe Tag, type: :model do
subscriber = ->(*args) { queries << args.last.fetch(:sql) }
ActiveSupport::Notifications.subscribed(subscriber, 'sql.active_record') do
expect(loaded.has_description?(language_code: 'ja')).to be(true)
expect(loaded.has_description?(language_code: 'en')).to be(false)
expect(loaded.has_description?(language_code: 'ain')).to be(false)
end
expect(queries).to be_empty
end
it 'keeps language selection generic without enabling another Language record' do
tag.association(:tag_descriptions).target = [
TagDescription.new(tag:, language_code: 'en', body: '', version_no: 1)]
TagDescription.new(tag:, language_code: 'ain', body: '', version_no: 1)]
tag.association(:tag_descriptions).loaded!
expect(tag.has_description?(language_code: 'en')).to be(true)
expect(tag.has_description?(language_code: 'ain')).to be(true)
expect(tag.has_description?(language_code: 'ja')).to be(false)
end
end
@@ -61,33 +61,32 @@ RSpec.describe Tag, type: :model do
end
describe '.normalise_tags!' do
include_context 'English locale'
include_context 'Ainu locale'
it 'canonicalises aliases in the supplied language only' do
pending '日本語以外の Locale 有効化は別課題で実装する'
prepare_english_reference!
prepare_ainu_reference!
nipponese = create(:tag, primary_name: 'nipponese_canonical')
english_name = create(:tag_name, name: 'english_canonical',
language_code: 'en', script_code: 'Latn')
english = create(:tag, primary_tag_name: english_name)
primary_tag_name_for(english, 'ja').update!(
name: 'english_in_nipponese', auto_generated: false)
english_locale = locale
ainu_name = create(:tag_name, name: 'ainu_canonical',
language_code: 'ain', script_code: 'Kana')
ainu = create(:tag, primary_tag_name: ainu_name)
primary_tag_name_for(ainu, 'ja').update!(
name: 'ainu_in_nipponese', auto_generated: false)
ainu_locale = locale
nipponese_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: nipponese)
english_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: english,
language_code: 'en', script_code: 'Latn')
ainu_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: ainu,
language_code: 'ain', script_code: 'Kana')
expect(nipponese_alias.reload).to have_attributes(
tag_id: nipponese.id,
primary_flg: false, language_code: 'ja')
expect(english_alias.reload).to have_attributes(
tag_id: english.id,
primary_flg: false, language_code: 'en')
expect(ainu_alias.reload).to have_attributes(
tag_id: ainu.id,
primary_flg: false, language_code: 'ain')
expect(described_class.normalise_tags!(
english_locale, ['shared_alias'],
with_tagme: false, with_no_deerjikist: false)).to eq([english])
ainu_locale, ['shared_alias'],
with_tagme: false, with_no_deerjikist: false)).to eq([ainu])
expect(described_class.normalise_tags!(
Locale.nipponese, ['shared_alias'],
with_tagme: false, with_no_deerjikist: false)).to eq([nipponese])
@@ -253,129 +252,120 @@ RSpec.describe Tag, type: :model do
describe '.find_or_create_by_tag_name!' do
context 'with an explicit locale' do
include_context 'English locale'
include_context 'Ainu locale'
it 'creates the representative name with locale attributes and ownership' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
tag = described_class.find_or_create_by_tag_name!(
locale, 'english_name', category: :character)
locale, 'ainu_name', category: :character)
expect(primary_tag_name_for(tag, 'en')).to have_attributes(
language_code: 'en', script_code: 'Latn',
expect(primary_tag_name_for(tag, 'ain')).to have_attributes(
language_code: 'ain', script_code: 'Kana',
primary_flg: true, tag_id: tag.id)
expect(tag.category).to eq('character')
end
it 'uses the English primary as the source for a generated Nipponese name' do
pending '日本語以外の Locale 有効化は別課題で実装する'
it 'uses a Japanese fallback for an Ainu-origin tag without reverse transcription' do
locale
allow(TagName).to receive(:generate_name).and_return('generated_nipponese')
allow(TagName).to receive(:generate_name).and_call_original
tag = described_class.find_or_create_by_tag_name!(
locale, 'english_origin', category: :general)
locale, 'ainu_origin', category: :general)
expect(TagName.where(tag:, language_code: 'en', primary_flg: true))
expect(TagName.where(tag:, language_code: 'ain', primary_flg: true))
.to contain_exactly(have_attributes(
name: 'english_origin', auto_generated: false))
name: 'ainu_origin', auto_generated: false))
expect(TagName.where(tag:, language_code: 'ja', primary_flg: true))
.to contain_exactly(have_attributes(
name: 'generated_nipponese', auto_generated: true))
source_tag_name = TagName.find_by!(tag:, language_code: 'en', primary_flg: true)
name: "Tag_##{ tag.id }", auto_generated: true))
expect(TagName).to have_received(:generate_name)
.with(Locale.nipponese, tag, source_tag_name)
.with(Locale.nipponese, tag, nil)
end
it 'gives a new tag one primary name for every active language' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
tag = described_class.find_or_create_by_tag_name!(
Locale.nipponese, 'new_multilingual_tag', category: :general)
expect(TagName.where(tag:, language_code: 'ja', primary_flg: true).count).to eq(1)
expect(TagName.where(tag:, language_code: 'en', primary_flg: true).count).to eq(1)
expect(TagName.where(tag:, language_code: 'ain', primary_flg: true).count).to eq(1)
end
it 'creates one primary per language when multiple locales use English' do
pending '日本語以外の Locale 有効化は別課題で実装する'
it 'creates one primary per language when multiple locales use Ainu' do
locale
described_class.find_or_create_by_tag_name!(
locale, 'before_second_english_locale', category: :general)
Locale.create!(code: 'en-x-rspec', language_code: 'en',
script_code: 'Latn', region_code: 826,
variants: 'x-rspec', name: 'English')
locale, 'before_second_ainu_locale', category: :general)
tag = described_class.find_or_create_by_tag_name!(
locale, 'one_english_primary', category: :general)
locale, 'one_ainu_primary', category: :general)
TagNameCompletion.for_tag!(tag, locales: [locale, locale.dup, Locale.nipponese])
expect(TagName.where(tag:, language_code: 'en', primary_flg: true).count).to eq(1)
expect(TagName.where(tag:, language_code: 'ain', primary_flg: true).count).to eq(1)
expect(TagName.where(tag:, language_code: 'ja', primary_flg: true).count).to eq(1)
end
it 'rolls back a new tag and requested name if name generation fails' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
original_tag_count = described_class.count
allow(TagName).to receive(:generate_name).and_raise('generation failed')
expect {
described_class.find_or_create_by_tag_name!(
locale, 'atomic_english_name', category: :general)
locale, 'atomic_ainu_name', category: :general)
}.to raise_error('generation failed')
expect(described_class.count).to eq(original_tag_count)
expect(TagName.where(language_code: 'en', name: 'atomic_english_name')).to be_empty
expect(TagName.where(language_code: 'ain', name: 'atomic_ainu_name')).to be_empty
expect(described_class.joins(:tag_names)
.where(tag_names: { name: 'atomic_english_name' })).to be_empty
.where(tag_names: { name: 'atomic_ainu_name' })).to be_empty
end
it 'creates separate tag identities for the same name in different languages' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
nipponese = described_class.find_or_create_by_tag_name!(
Locale.nipponese, 'same_name', category: :general)
english = described_class.find_or_create_by_tag_name!(
primary_tag_name_for(nipponese, 'ain').update!(
name: 'other_ainu_name', auto_generated: false)
ainu = described_class.find_or_create_by_tag_name!(
locale, 'same_name', category: :character)
expect(english).not_to eq(nipponese)
expect(primary_tag_name_for(english, 'en')).to have_attributes(
name: 'same_name', tag_id: english.id, language_code: 'en', primary_flg: true)
expect(ainu).not_to eq(nipponese)
expect(primary_tag_name_for(ainu, 'ain')).to have_attributes(
name: 'same_name', tag_id: ainu.id, language_code: 'ain', primary_flg: true)
expect(primary_tag_name_for(nipponese, 'ja')).to have_attributes(
name: 'same_name', tag_id: nipponese.id, language_code: 'ja', primary_flg: true)
expect(described_class.find_or_create_by_tag_name!(
locale, 'same_name', category: :general)).to eq(english)
locale, 'same_name', category: :general)).to eq(ainu)
expect(described_class.find_or_create_by_tag_name!(
Locale.nipponese, 'same_name', category: :general)).to eq(nipponese)
end
it 'preserves the V1 representative when looking up another primary language' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
tag = create(:tag, primary_name: '日本語代表名')
representative_id = tag.tag_name('ja')&.id
english = primary_tag_name_for(tag, 'en')
english.update!(name: 'english_primary', auto_generated: false)
ainu = primary_tag_name_for(tag, 'ain')
ainu.update!(name: 'ainu_primary', auto_generated: false)
found = described_class.find_or_create_by_tag_name!(
locale, english.name, category: :general)
locale, ainu.name, category: :general)
expect(found).to eq(tag)
expect(tag.reload.tag_name('ja')&.id).to eq(representative_id)
expect(english.reload.tag_id).to eq(tag.id)
expect(ainu.reload.tag_id).to eq(tag.id)
end
it 'normalises names using the supplied locale' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
tags = described_class.normalise_tags!(
locale, ['character:normalised_english'],
locale, ['character:normalised_ainu'],
with_tagme: false, with_no_deerjikist: false)
expect(tags.length).to eq(1)
expect(primary_tag_name_for(tags.first, 'en')).to have_attributes(
name: 'normalised_english', language_code: 'en', script_code: 'Latn',
expect(primary_tag_name_for(tags.first, 'ain')).to have_attributes(
name: 'normalised_ainu', language_code: 'ain', script_code: 'Kana',
primary_flg: true, tag_id: tags.first.id)
end
end
@@ -444,26 +434,25 @@ RSpec.describe Tag, type: :model do
end
context 'with names in multiple languages' do
include_context 'English locale'
include_context 'Ainu locale'
it 'keeps the target primary and moves source names as aliases' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
english_primary = primary_tag_name_for(target_tag, 'en')
source_primary = primary_tag_name_for(source_tag, 'en')
ainu_primary = primary_tag_name_for(target_tag, 'ain')
source_primary = primary_tag_name_for(source_tag, 'ain')
source_alias = create(:tag_name, :alias, tag: source_tag,
name: 'source_english_alias', language_code: 'en', script_code: 'Latn')
name: 'source_ainu_alias', language_code: 'ain', script_code: 'Kana')
expect([english_primary, source_primary]).to all(have_attributes(
expect([ainu_primary, source_primary]).to all(have_attributes(
primary_flg: true, auto_generated: true))
described_class.merge_tags!(target_tag, [source_tag])
expect(primary_tag_name_for(target_tag.reload, 'en')).to eq(english_primary)
expect(TagName.where(tag: target_tag, language_code: 'en', primary_flg: true))
.to contain_exactly(english_primary)
expect(primary_tag_name_for(target_tag.reload, 'ain')).to eq(ainu_primary)
expect(TagName.where(tag: target_tag, language_code: 'ain', primary_flg: true))
.to contain_exactly(ainu_primary)
expect([source_primary.reload, source_alias.reload]).to all(have_attributes(
tag_id: target_tag.id, language_code: 'en', primary_flg: false))
tag_id: target_tag.id, language_code: 'ain', primary_flg: false))
expect(TagName.where(tag: target_tag, language_code: 'ja', primary_flg: true))
.to contain_exactly(primary_tag_name_for(target_tag, 'ja'))
expect(source_tag_name.reload).to have_attributes(
+54
ファイルの表示
@@ -0,0 +1,54 @@
require 'rails_helper'
RSpec.describe 'API locale resolution', type: :request do
let!(:tag) { create(:tag, primary_name: '日本語正本') }
it 'defaults an omitted locale to Japanese' do
get "/tags/#{ tag.id }"
expect(response).to have_http_status(:ok)
expect(json.fetch('name')).to eq('日本語正本')
end
it 'accepts an explicitly requested active Japanese locale' do
get "/tags/#{ tag.id }", params: { locale: 'ja' }
expect(response).to have_http_status(:ok)
expect(json.fetch('name')).to eq('日本語正本')
end
['', 'en', 'ain', 'Jpan', 'JA', 'unknown', ['ja']].each do |code|
it "rejects invalid locale #{ code.inspect } instead of defaulting to Japanese" do
get "/tags/#{ tag.id }", params: { locale: code }
expect(response).to have_http_status(:not_found)
expect(response.body).not_to include('日本語正本')
end
end
it 'rejects a supported locale that has not been registered' do
get "/tags/#{ tag.id }", params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:not_found)
end
it 'rejects an inactive locale' do
Locale.nipponese.update!(deprecated_at: Time.current)
get "/tags/#{ tag.id }", params: { locale: 'ja' }
expect(response).to have_http_status(:not_found)
end
it 'does not write posts or tag names under an invalid locale' do
sign_in_as(create_member_user!)
expect do
post '/posts', params: {
locale: 'en', title: 'invalid locale', url: 'https://example.com/invalid-locale',
tags: 'unwritten_tag' }
end.to change(Post, :count).by(0).and change(TagName, :count).by(0)
expect(response).to have_http_status(:not_found)
end
end
+61 -78
ファイルの表示
@@ -1,7 +1,7 @@
require 'rails_helper'
RSpec.describe 'Locale propagation on write paths', type: :request do
include_context 'English locale'
include_context 'Ainu locale'
let(:member) { create(:user, :member) }
@@ -54,13 +54,12 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
end
end
['en', nil].each do |requested_locale|
['ain-Kana', nil].each do |requested_locale|
context "with locale #{ requested_locale.inspect }" do
let(:locale_params) { requested_locale ? { locale: requested_locale } : { } }
let(:expected_locale) { requested_locale ? locale : Locale.nipponese }
it 'creates post tags in the requested language or Nipponese fallback' do
pending '日本語以外の Locale 有効化は別課題で実装する' if requested_locale
locale if requested_locale
post '/posts', params: locale_params.merge(
title: 'Locale post', url: 'https://example.com/locale-post',
@@ -72,7 +71,6 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
end
it 'updates post tags in the requested language or Nipponese fallback' do
pending '日本語以外の Locale 有効化は別課題で実装する' if requested_locale
locale if requested_locale
record = create(:post)
PostVersionRecorder.record!(post: record, event_type: :create, created_by_user: member)
@@ -87,7 +85,6 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
end
it 'passes the resolved locale to bulk creation' do
pending '日本語以外の Locale 有効化は別課題で実装する' if requested_locale
locale if requested_locale
# Worker propagation is exercised in post_bulk_creator_spec.
expect_any_instance_of(PostBulkCreator).to receive(:run)
@@ -102,7 +99,6 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
end
it 'creates materials with tags in the resolved locale' do
pending '日本語以外の Locale 有効化は別課題で実装する' if requested_locale
locale if requested_locale
post '/materials', params: locale_params.merge(
tag: 'locale_material', url: 'https://example.com/material')
@@ -113,7 +109,6 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
end
it 'updates materials with tags in the resolved locale' do
pending '日本語以外の Locale 有効化は別課題で実装する' if requested_locale
locale if requested_locale
tag = create(:tag, category: :material)
material = Material.create!(tag:, url: 'https://example.com/material')
@@ -127,7 +122,6 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
end
it 'links Nico external tags to internal tags in the resolved locale' do
pending '日本語以外の Locale 有効化は別課題で実装する' if requested_locale
locale if requested_locale
external = create(:external_tag)
NicoTagVersionRecorder.record!(
@@ -141,7 +135,6 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
end
it 'creates parent tags in the resolved locale' do
pending '日本語以外の Locale 有効化は別課題で実装する' if requested_locale
locale if requested_locale
tag = create(:tag)
@@ -157,10 +150,10 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
end
context 'with existing names in both languages' do
let(:english_name) do
create(:tag_name, name: 'shared_name', language_code: 'en', script_code: 'Latn')
let(:ainu_name) do
create(:tag_name, name: 'shared_name', language_code: 'ain', script_code: 'Kana')
end
let(:english_tag) { create(:tag, primary_tag_name: english_name, category: :material) }
let(:ainu_tag) { create(:tag, primary_tag_name: ainu_name, category: :material) }
let(:nipponese_tag) do
create(:tag, primary_name: 'temporary_nipponese', category: :general).tap do |tag|
# Isolate lookup from the separately tested uniqueness validation.
@@ -168,173 +161,163 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
end
end
it 'plans and creates the post using the English identity and category' do
pending '日本語以外の Locale 有効化は別課題で実装する'
it 'plans and creates the post using the Ainu identity and category' do
locale
english_tag
ainu_tag
nipponese_tag
post '/posts', params: {
locale: 'en', title: 'English identity',
url: 'https://example.com/english-identity', tags: 'shared_name', parent_post_ids: '' }
locale: 'ain-Kana', title: 'Ainu identity',
url: 'https://example.com/ainu-identity', tags: 'shared_name', parent_post_ids: '' }
expect(response).to have_http_status(:created), response.body
record = Post.find(json.fetch('id'))
expect(record.tags).to include(english_tag)
expect(record.tags).to include(ainu_tag)
expect(record.tags).not_to include(nipponese_tag)
expect(english_tag.reload.category).to eq('material')
expect(ainu_tag.reload.category).to eq('material')
expect(nipponese_tag.reload.category).to eq('general')
end
it 'updates the post using the English identity' do
pending '日本語以外の Locale 有効化は別課題で実装する'
it 'updates the post using the Ainu identity' do
locale
english_tag
ainu_tag
nipponese_tag
record = create(:post)
PostVersionRecorder.record!(post: record, event_type: :create, created_by_user: member)
put "/posts/#{ record.id }", params: {
locale: 'en', title: record.title, tags: 'shared_name', parent_post_ids: '',
locale: 'ain-Kana', title: record.title, tags: 'shared_name', parent_post_ids: '',
base_version_no: record.reload.version_no }
expect(response).to have_http_status(:ok), response.body
expect(record.reload.tags).to include(english_tag)
expect(record.reload.tags).to include(ainu_tag)
expect(record.tags).not_to include(nipponese_tag)
end
it 'resolves the English material tag' do
pending '日本語以外の Locale 有効化は別課題で実装する'
it 'resolves the Ainu material tag' do
locale
english_tag
ainu_tag
nipponese_tag
post '/materials', params: {
locale: 'en', tag: 'shared_name', url: 'https://example.com/english-material' }
locale: 'ain-Kana', tag: 'shared_name', url: 'https://example.com/ainu-material' }
expect(response).to have_http_status(:created), response.body
expect(Material.find(json.fetch('id')).tag).to eq(english_tag)
expect(Material.find(json.fetch('id')).tag).to eq(ainu_tag)
expect(Material.where(tag: nipponese_tag)).to be_empty
end
it 'resolves the English parent tag' do
pending '日本語以外の Locale 有効化は別課題で実装する'
it 'resolves the Ainu parent tag' do
locale
english_tag
ainu_tag
nipponese_tag
child = create(:tag)
put "/tags/#{ child.id }", params: {
locale: 'en', name: primary_tag_name_for(child, 'ja').name,
locale: 'ain-Kana', name: primary_tag_name_for(child, 'ja').name,
category: child.category, deprecated: false,
aliases: '', parent_tags: 'shared_name' }
expect(response).to have_http_status(:ok), response.body
expect(TagImplication.where(tag: child).pluck(:parent_tag_id)).to eq([english_tag.id])
expect(TagImplication.where(tag: child).pluck(:parent_tag_id)).to eq([ainu_tag.id])
end
it 'resolves the English internal tag for a Nico link' do
pending '日本語以外の Locale 有効化は別課題で実装する'
it 'resolves the Ainu internal tag for a Nico link' do
locale
english_tag
ainu_tag
nipponese_tag
external = create(:external_tag)
NicoTagVersionRecorder.record!(
external_tag: external, event_type: :create, created_by_user: member)
put "/tags/nico/#{ external.id }", params: { locale: 'en', tags: 'shared_name' }
put "/tags/nico/#{ external.id }", params: { locale: 'ain-Kana', tags: 'shared_name' }
expect(response).to have_http_status(:ok), response.body
expect(external.reload.linked_tags).to contain_exactly(english_tag)
expect(external.reload.linked_tags).to contain_exactly(ainu_tag)
end
end
context 'when renaming or updating aliases in English' do
let(:english_name) do
create(:tag_name, name: 'english_original', language_code: 'en', script_code: 'Latn')
context 'when renaming or updating aliases in Ainu' do
let(:ainu_name) do
create(:tag_name, name: 'ainu_original', language_code: 'ain', script_code: 'Kana')
end
let(:tag) { create(:tag, primary_tag_name: english_name) }
let(:tag) { create(:tag, primary_tag_name: ainu_name) }
it 'marks a human-renamed generated primary as no longer auto-generated' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
generated_tag = create(:tag, primary_name: 'rename_generated_source')
generated_name = primary_tag_name_for(generated_tag, 'en')
generated_name = primary_tag_name_for(generated_tag, 'ain')
expect(generated_name.auto_generated).to be(true)
patch "/tags/#{ generated_tag.id }", params: {
locale: 'en', name: 'human_english_name' }
locale: 'ain-Kana', name: 'human_ainu_name' }
expect(response).to have_http_status(:ok), response.body
expect(generated_name.reload).to have_attributes(
name: 'human_english_name', auto_generated: false)
name: 'human_ainu_name', auto_generated: false)
end
it 'does not steal another tag primary for an alias' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
other_tag = create(:tag, primary_name: 'other_nipponese')
other_primary = primary_tag_name_for(other_tag, 'en')
other_primary.update!(name: 'other_english_primary', auto_generated: false)
create(:tag_name, :alias, tag:, name: 'existing_english_alias',
language_code: 'en', script_code: 'Latn')
primary_name_before = primary_tag_name_for(tag, 'en').name
other_primary = primary_tag_name_for(other_tag, 'ain')
other_primary.update!(name: 'other_ainu_primary', auto_generated: false)
create(:tag_name, :alias, tag:, name: 'existing_ainu_alias',
language_code: 'ain', script_code: 'Kana')
primary_name_before = primary_tag_name_for(tag, 'ain').name
aliases_before =
tag.tag_names.where(language_code: 'en', primary_flg: false)
tag.tag_names.where(language_code: 'ain', primary_flg: false)
.order(:name).pluck(:name)
category_before = tag.category
put "/tags/#{ tag.id }", params: {
locale: 'en', name: 'renamed_before_alias_collision',
locale: 'ain-Kana', name: 'renamed_before_alias_collision',
category: 'character', deprecated: false,
aliases: 'other_english_primary', parent_tags: '' }
aliases: 'other_ainu_primary', parent_tags: '' }
aggregate_failures do
expect(response).to have_http_status(:unprocessable_entity)
expect(other_primary.reload).to have_attributes(
tag_id: other_tag.id, language_code: 'en', primary_flg: true)
expect(TagName.where(tag: other_tag, language_code: 'en', primary_flg: true))
tag_id: other_tag.id, language_code: 'ain', primary_flg: true)
expect(TagName.where(tag: other_tag, language_code: 'ain', primary_flg: true))
.to contain_exactly(other_primary)
expect(primary_tag_name_for(tag.reload, 'en').name).to eq(primary_name_before)
expect(primary_tag_name_for(tag.reload, 'ain').name).to eq(primary_name_before)
expect(tag.category).to eq(category_before)
aliases_after =
tag.tag_names.where(language_code: 'en', primary_flg: false)
tag.tag_names.where(language_code: 'ain', primary_flg: false)
.order(:name).pluck(:name)
expect(aliases_after).to eq(aliases_before)
end
end
it 'allows a rename to a name already used in Nipponese' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
nipponese = create(:tag, primary_name: 'rename_target')
patch "/tags/#{ tag.id }", params: { locale: 'en', name: 'rename_target' }
patch "/tags/#{ tag.id }", params: { locale: 'ain-Kana', name: 'rename_target' }
expect(response).to have_http_status(:ok), response.body
expect(primary_tag_name_for(tag.reload, 'en')).to have_attributes(
name: 'rename_target', language_code: 'en', tag_id: tag.id)
expect(primary_tag_name_for(tag.reload, 'ain')).to have_attributes(
name: 'rename_target', language_code: 'ain', tag_id: tag.id)
expect(primary_tag_name_for(nipponese.reload, 'ja')).to have_attributes(
name: 'rename_target', language_code: 'ja', tag_id: nipponese.id)
end
it 'creates aliases in the requested language' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
put "/tags/#{ tag.id }", params: {
locale: 'en', name: english_name.name,
locale: 'ain-Kana', name: ainu_name.name,
category: tag.category, deprecated: false,
aliases: 'english_alias', parent_tags: '' }
aliases: 'ainu_alias', parent_tags: '' }
expect(response).to have_http_status(:ok), response.body
expect(TagName.find_by!(language_code: 'en', name: 'english_alias'))
expect(TagName.find_by!(language_code: 'ain', name: 'ainu_alias'))
.to have_attributes(
script_code: 'Latn',
tag_id: tag.id, primary_flg: false, language_code: 'en')
primary = TagName.find_by!(tag_id: tag.id, language_code: 'en', primary_flg: true)
expect(primary).to have_attributes(name: 'english_original', script_code: 'Latn')
script_code: 'Kana',
tag_id: tag.id, primary_flg: false, language_code: 'ain')
primary = TagName.find_by!(tag_id: tag.id, language_code: 'ain', primary_flg: true)
expect(primary).to have_attributes(name: 'ainu_original', script_code: 'Kana')
end
it 'does not take an alias from another language' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
nipponese = create(:tag, primary_name: 'nipponese_owner')
nipponese_alias = create(:tag_name, :alias, name: 'shared_alias',
@@ -344,7 +327,7 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
primary_flg: false, language_code: 'ja')
put "/tags/#{ tag.id }", params: {
locale: 'en', name: english_name.name,
locale: 'ain-Kana', name: ainu_name.name,
category: tag.category, deprecated: false,
aliases: 'shared_alias', parent_tags: '' }
@@ -352,12 +335,12 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
expect(nipponese_alias.reload).to have_attributes(
tag_id: nipponese.id,
primary_flg: false, language_code: 'ja')
expect(TagName.find_by!(language_code: 'en', name: 'shared_alias'))
expect(TagName.find_by!(language_code: 'ain', name: 'shared_alias'))
.to have_attributes(
tag_id: tag.id, script_code: 'Latn',
primary_flg: false, language_code: 'en')
tag_id: tag.id, script_code: 'Kana',
primary_flg: false, language_code: 'ain')
expect(TagName.where(name: 'shared_alias').pluck(:language_code, :tag_id))
.to contain_exactly(['ja', nipponese.id], ['en', tag.id])
.to contain_exactly(['ja', nipponese.id], ['ain', tag.id])
end
end
end
+16 -15
ファイルの表示
@@ -22,7 +22,7 @@ RSpec.describe 'Tag group locale contracts', type: :request do
it 'uses selected-language names in native collection and nested placements' do
["/tag_groups/#{ group.id }", '/tag_groups'].each do |path|
get path, params: { locale: 'ain' }
get path, params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:ok)
representation = json.is_a?(Array) ? json.sole : json
expect(representation.fetch('members').sole.fetch('tag').fetch('name'))
@@ -37,14 +37,14 @@ RSpec.describe 'Tag group locale contracts', type: :request do
it 'uses snapshot names recursively for history and current names for the latest view' do
version = group.versions.sole
ainu_name.update!(name: 'renamed_ainu')
get "/wiki/#{ group.id }", params: { locale: 'ain' }
get "/wiki/#{ group.id }", params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:ok)
expect(json.fetch('body')).to include('renamed_ainu')
get "/wiki/#{ group.id }", params: { locale: 'ain', version: version.id }
get "/wiki/#{ group.id }", params: { locale: 'ain-Kana', version: version.id }
expect(response).to have_http_status(:ok)
expect(json.fetch('body').scan('ainu_member').length).to eq(2)
expect(json.fetch('body')).not_to include('renamed_ainu', 'japanese_member')
get "/wiki/#{ group.id }/diff", params: { locale: 'ain', from: version.id }
get "/wiki/#{ group.id }/diff", params: { locale: 'ain-Kana', from: version.id }
expect(response).to have_http_status(:ok)
expect(json.fetch('diff').map { _1.fetch('type') }.uniq).to eq(['context'])
end
@@ -52,23 +52,24 @@ RSpec.describe 'Tag group locale contracts', type: :request do
['タググループ:', 'tag_group:', 'TAG_GROUP:'].each do |prefix|
it "round trips #{ prefix } through create, lookup, search and update" do
post '/wiki', params: {
locale: 'ain', title: "#{ prefix }parent", body: '- tag_group:locale group' },
locale: 'ain-Kana', title: "#{ prefix }parent", body: '- tag_group:locale group' },
headers: headers
expect(response).to have_http_status(:created)
id = json.fetch('id')
expect(json.fetch('body')).to include('タググループ:locale group')
path = "/wiki/title/#{ CGI.escapeURIComponent("#{ prefix }parent") }"
get path, params: { locale: 'ain' }
get path, params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:ok)
expect(json.fetch('id')).to eq(id)
get "#{ path }/exists", params: { locale: 'ain' }
get "#{ path }/exists", params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:no_content)
get '/wiki/search', params: { locale: 'ain', title: "#{ prefix }parent" }
get '/wiki/search', params: { locale: 'ain-Kana', title: "#{ prefix }parent" }
expect(json.map { _1.fetch('id') }).to eq([id])
put "/wiki/#{ id }", params: {
locale: 'ain', title: "#{ prefix }renamed", body: '- ainu_member' }, headers: headers
locale: 'ain-Kana', title: "#{ prefix }renamed", body: '- ainu_member' },
headers: headers
expect(response).to have_http_status(:ok)
get "/tag_groups/#{ id }", params: { locale: 'ain' }
get "/tag_groups/#{ id }", params: { locale: 'ain-Kana' }
expect(json.fetch('members').sole.fetch('tag').fetch('name')).to eq('ainu_member')
end
end
@@ -81,7 +82,7 @@ RSpec.describe 'Tag group locale contracts', type: :request do
end.to raise_error(TagGroupMarkdownParser::InvalidDocument)
before_state = group_state
put "/wiki/#{ group.id }", params: {
locale: 'ain', title: 'タググループ:partial rename', body: '- japanese_member' },
locale: 'ain-Kana', title: 'タググループ:partial rename', body: '- japanese_member' },
headers: headers
expect(response).to have_http_status(:unprocessable_entity)
expect(group_state).to eq(before_state)
@@ -89,15 +90,15 @@ RSpec.describe 'Tag group locale contracts', type: :request do
it 'passes locale into native validation and facade compatibility checks' do
post '/tag_groups', params: {
locale: 'ain', name: 'native', description: nil, members:, placements: },
locale: 'ain-Kana', name: 'native', description: nil, members:, placements: },
headers:, as: :json
expect(response).to have_http_status(:created)
id = json.fetch('id')
expect(json.fetch('members').sole.fetch('tag').fetch('name')).to eq('ainu_member')
get "/wiki/#{ id }", params: { locale: 'ain' }
get "/wiki/#{ id }", params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:ok)
body = json.fetch('body')
put "/wiki/#{ id }", params: { locale: 'ain', title: 'tag_group:native', body: },
put "/wiki/#{ id }", params: { locale: 'ain-Kana', title: 'tag_group:native', body: },
headers: headers
expect(response).to have_http_status(:ok)
expect(TagGroup.find(id).version_no).to eq(1)
@@ -115,7 +116,7 @@ RSpec.describe 'Tag group locale contracts', type: :request do
representation = TagGroupRepr.historical(ainu_locale, version)
expect(representation.fetch('members').sole.fetch('tag').fetch('name')).to be_nil
expect(representation.fetch('placements').sole.fetch('tag').fetch('name')).to be_nil
get "/wiki/#{ old_group.id }", params: { locale: 'ain', version: version.id }
get "/wiki/#{ old_group.id }", params: { locale: 'ain-Kana', version: version.id }
expect(response).to have_http_status(:unprocessable_entity)
expect(response.body).not_to include('historical_japanese', 'current_ainu')
end
+20 -20
ファイルの表示
@@ -20,12 +20,12 @@ RSpec.describe 'Wiki and native API locale contracts', type: :request do
['/wiki', '/wiki/search'].each do |path|
it "filters #{ path } by the selected language" do
get path, params: { locale: 'ain' }
get path, params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:ok)
expect(json.map { _1.fetch('id') }).to eq([ainu.id])
expect(json.sole).to include('title' => 'ainu_title', 'body' => 'Ainu body')
get path, params: { locale: 'ain', title: 'japanese' }
get path, params: { locale: 'ain-Kana', title: 'japanese' }
expect(response).to have_http_status(:ok)
expect(json).to eq([])
end
@@ -39,17 +39,17 @@ RSpec.describe 'Wiki and native API locale contracts', type: :request do
it 'resolves titles and aliases only in the selected language in both APIs' do
['/wiki/title/japanese_title', '/tags/name/japanese_title/description'].each do |path|
get path, params: { locale: 'ain' }
get path, params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:not_found)
end
['/wiki/title/ainu_title', '/tags/name/ainu_title/description'].each do |path|
get path, params: { locale: 'ain' }
get path, params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:ok)
expect(json.fetch('body')).to eq('Ainu body')
end
get '/wiki/title/ainu_title/exists', params: { locale: 'ain' }
get '/wiki/title/ainu_title/exists', params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:no_content)
get '/wiki/title/japanese_title/exists', params: { locale: 'ain' }
get '/wiki/title/japanese_title/exists', params: { locale: 'ain-Kana' }
expect(response).to have_http_status(:not_found)
end
@@ -57,14 +57,14 @@ RSpec.describe 'Wiki and native API locale contracts', type: :request do
revision = current_description_version(japanese)
["/wiki/#{ japanese.id }", "/wiki/#{ japanese.id }/exists",
"/wiki/#{ japanese.id }/diff"].each do |path|
get path, params: { locale: 'ain', from: revision.id }
get path, params: { locale: 'ain-Kana', from: revision.id }
expect(response).to have_http_status(:not_found)
expect(response.body).not_to include('Japanese body')
end
get '/wiki/changes', params: { locale: 'ain', id: japanese.id }
get '/wiki/changes', params: { locale: 'ain-Kana', id: japanese.id }
expect(response).to have_http_status(:ok)
expect(json).to eq([])
get '/wiki/changes', params: { locale: 'ain' }
get '/wiki/changes', params: { locale: 'ain-Kana' }
expect(json.map { _1.fetch('revision_id') })
.to eq([current_description_version(ainu).id])
end
@@ -73,7 +73,7 @@ RSpec.describe 'Wiki and native API locale contracts', type: :request do
snapshot = [japanese.attributes, ainu.attributes, ainu_name.attributes]
expect do
put "/wiki/#{ japanese.id }", params: {
locale: 'ain', title: 'renamed', body: 'wrong target' }, headers: headers
locale: 'ain-Kana', title: 'renamed', body: 'wrong target' }, headers: headers
end.not_to change(TagDescriptionVersion, :count)
expect(response).to have_http_status(:not_found)
expect([japanese.reload.attributes, ainu.reload.attributes, ainu_name.reload.attributes])
@@ -89,10 +89,10 @@ RSpec.describe 'Wiki and native API locale contracts', type: :request do
snapshot = original.attributes
expect do
if api == :wiki
post '/wiki', params: { locale: 'ain', title: 'other_ainu', body: 'created' },
post '/wiki', params: { locale: 'ain-Kana', title: 'other_ainu', body: 'created' },
headers: headers
else
put "/tags/#{ other.id }/description", params: { locale: 'ain', body: 'created' },
put "/tags/#{ other.id }/description", params: { locale: 'ain-Kana', body: 'created' },
headers: headers
end
end.to change(TagDescription, :count).by(1)
@@ -108,20 +108,20 @@ RSpec.describe 'Wiki and native API locale contracts', type: :request do
it 'shares selected-language edits and revision identity across both APIs' do
snapshot = japanese.attributes
first = current_description_version(ainu)
put "/tags/#{ tag.id }/description", params: { locale: 'ain', body: 'native edit' },
put "/tags/#{ tag.id }/description", params: { locale: 'ain-Kana', body: 'native edit' },
headers: headers
expect(response).to have_http_status(:ok)
second = current_description_version(ainu)
expect(second).to have_attributes(language_code: 'ain', version_no: 2)
expect(second.id).not_to eq(first.id)
get "/wiki/#{ ainu.id }", params: { locale: 'ain' }
get "/wiki/#{ ainu.id }", params: { locale: 'ain-Kana' }
expect(json).to include('body' => 'native edit', 'revision_id' => second.id)
put "/wiki/#{ ainu.id }", params: {
locale: 'ain', title: 'ainu_title', body: 'facade edit',
locale: 'ain-Kana', title: 'ainu_title', body: 'facade edit',
base_revision_id: second.id }, headers: headers
expect(response).to have_http_status(:ok)
get "/tags/#{ tag.id }/description", params: { locale: 'ain' }
get "/tags/#{ tag.id }/description", params: { locale: 'ain-Kana' }
expect(json).to include('body' => 'facade edit', 'language_code' => 'ain', 'version_no' => 3)
expect(japanese.reload.attributes).to eq(snapshot)
end
@@ -133,7 +133,7 @@ RSpec.describe 'Wiki and native API locale contracts', type: :request do
snapshot = [ainu.reload.attributes, ainu_name.attributes, japanese.attributes]
expect do
put "/wiki/#{ ainu.id }", params: {
locale: 'ain', title: 'conflicting_name', body: 'current',
locale: 'ain-Kana', title: 'conflicting_name', body: 'current',
base_revision_id: stale.id }, headers: headers
end.to change(TagDescriptionVersion, :count).by(0).and change(TagVersion, :count).by(0)
expect(response).to have_http_status(:conflict)
@@ -145,14 +145,14 @@ RSpec.describe 'Wiki and native API locale contracts', type: :request do
first = current_description_version(ainu)
TagDescriptionOperation.update!(
ainu_locale, tag:, body: 'later', updated_by_user: user)
get "/wiki/#{ ainu.id }", params: { locale: 'ain', version: first.id }
get "/wiki/#{ ainu.id }", params: { locale: 'ain-Kana', version: first.id }
expect(response).to have_http_status(:ok)
expect(json).to include('body' => 'Ainu body', 'revision_id' => first.id)
get "/wiki/#{ ainu.id }/diff", params: { locale: 'ain', from: first.id }
get "/wiki/#{ ainu.id }/diff", params: { locale: 'ain-Kana', from: first.id }
expect(response).to have_http_status(:ok)
expect(json.fetch('newer_revision_id')).to eq(current_description_version(ainu).id)
get "/wiki/#{ ainu.id }/diff", params: {
locale: 'ain', from: current_description_version(japanese).id }
locale: 'ain-Kana', from: current_description_version(japanese).id }
expect(response).to have_http_status(:not_found)
end
end
+2 -3
ファイルの表示
@@ -1,7 +1,7 @@
require 'rails_helper'
RSpec.describe PostBulkCreator do
include_context 'English locale'
include_context 'Ainu locale'
it 'limits workers to two and keeps failures in their request slots' do
locale = Locale.nipponese
@@ -67,8 +67,7 @@ RSpec.describe PostBulkCreator do
expect(results[3].dig(:post, :id)).to eq(4)
end
it 'passes a future English locale to preflight and creation in every worker' do
pending '日本語以外の Locale 有効化は別課題で実装する'
it 'passes a future Ainu locale to preflight and creation in every worker' do
locale
actor = instance_double(User, id: 123)
allow(User).to receive(:find).with(123).and_return(actor)
+19 -21
ファイルの表示
@@ -11,23 +11,22 @@ RSpec.describe PostCreatePlan do
end
context 'with an explicit locale' do
include_context 'English locale'
include_context 'Ainu locale'
it 'resolves the matching language identity and its parents' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
english_name = create(:tag_name, name: 'shared_name',
language_code: 'en', script_code: 'Latn')
english = create(:tag, primary_tag_name: english_name, category: :character)
ainu_name = create(:tag_name, name: 'shared_name',
language_code: 'ain', script_code: 'Kana')
ainu = create(:tag, primary_tag_name: ainu_name, category: :character)
nipponese = create(:tag, primary_name: 'temporary_nipponese', category: :general)
# The DB permits this identity; validation has its own contract spec.
primary_tag_name_for(nipponese, 'ja').update_columns(name: 'shared_name')
english_parent_name = create(:tag_name, name: 'english_parent',
language_code: 'en', script_code: 'Latn')
english_parent = create(:tag, primary_tag_name: english_parent_name,
ainu_parent_name = create(:tag_name, name: 'ainu_parent',
language_code: 'ain', script_code: 'Kana')
ainu_parent = create(:tag, primary_tag_name: ainu_parent_name,
category: :material)
nipponese_parent = create(:tag, primary_name: 'nipponese_parent', category: :general)
TagImplication.create!(tag: english, parent_tag: english_parent)
TagImplication.create!(tag: ainu, parent_tag: ainu_parent)
TagImplication.create!(tag: nipponese, parent_tag: nipponese_parent)
plan = described_class.new(attributes: { tags: 'shared_name' }).build!(locale)
@@ -35,35 +34,34 @@ RSpec.describe PostCreatePlan do
expect(plan[:direct_tag_specs]).to eq(
[{ name: 'shared_name', category: :character }])
expect(plan[:post_tag_specs]).to include(
{ name: primary_tag_name_for(english_parent, 'en').name, category: :material })
{ name: primary_tag_name_for(ainu_parent, 'ain').name, category: :material })
expect(plan[:post_tag_specs].pluck(:name))
.not_to include(primary_tag_name_for(nipponese_parent, 'ja').name)
end
it 'resolves aliases within the requested language' do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
nipponese = create(:tag, primary_name: 'nipponese_canonical')
english_name = create(:tag_name, name: 'english_canonical',
language_code: 'en', script_code: 'Latn')
english = create(:tag, primary_tag_name: english_name, category: :character)
ainu_name = create(:tag_name, name: 'ainu_canonical',
language_code: 'ain', script_code: 'Kana')
ainu = create(:tag, primary_tag_name: ainu_name, category: :character)
nipponese_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: nipponese)
english_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: english,
language_code: 'en', script_code: 'Latn')
ainu_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: ainu,
language_code: 'ain', script_code: 'Kana')
expect(nipponese_alias.reload).to have_attributes(
tag_id: nipponese.id,
primary_flg: false, language_code: 'ja')
expect(english_alias.reload).to have_attributes(
tag_id: english.id,
primary_flg: false, language_code: 'en')
expect(ainu_alias.reload).to have_attributes(
tag_id: ainu.id,
primary_flg: false, language_code: 'ain')
plan = described_class.new(attributes: { tags: 'shared_alias' }).build!(locale)
expect(plan[:direct_tag_specs]).to eq(
[{ name: english_name.name, category: :character }])
[{ name: ainu_name.name, category: :character }])
end
end
+3 -4
ファイルの表示
@@ -77,11 +77,10 @@ RSpec.describe PostCreator do
end
context 'with an explicit locale' do
include_context 'English locale'
include_context 'Ainu locale'
[false, true].each do |planned|
it "passes locale to tag creation with planned attributes: #{ planned }" do
pending '日本語以外の Locale 有効化は別課題で実装する'
locale
attributes = {
title: 'Locale propagation',
@@ -97,8 +96,8 @@ RSpec.describe PostCreator do
expect(Tag).to have_received(:find_or_create_by_tag_name!)
.with(locale, 'locale_character', category: :character).at_least(:once)
tag_name = TagName.find_by!(language_code: 'en', name: 'locale_character')
expect(tag_name).to have_attributes(script_code: 'Latn', primary_flg: true)
tag_name = TagName.find_by!(language_code: 'ain', name: 'locale_character')
expect(tag_name).to have_attributes(script_code: 'Kana', primary_flg: true)
expect(post.tags).to include(tag_name.tag)
end
end
+68
ファイルの表示
@@ -0,0 +1,68 @@
require 'rails_helper'
RSpec.describe TagNameCompletion do
let!(:language) { Language.create!(code: 'ain', name: 'ain') }
let!(:script) { Script.create!(code: 'Kana', name: 'Kana') }
let(:locale) do
Locale.create!(code: 'ain-Kana', language_code: 'ain', script_code: 'Kana',
region_code: 392, variants: '', name: 'ain-Kana')
end
it 'generates a phonetic primary with the required identity and provenance' do
tag = create(:tag, primary_name: '漢字あカ')
locale
expect(tag.reload.tag_name('ain')).to have_attributes(
name: 'カンチ_ア_カ', language_code: 'ain', script_code: 'Kana',
primary_flg: true, auto_generated: true, tag_id: tag.id)
end
it 'uses the exact fallback when the input cannot be converted' do
tag = create(:tag, primary_name: '😀')
locale
expect(tag.reload.name('ain')).to eq("Tag_##{ tag.id }")
end
it 'uses the exact fallback when a generated name collides with a human name' do
other = create(:tag, primary_name: 'other')
create(:tag_name, tag: other, name: 'AbC', language_code: 'ain', script_code: 'Kana')
tag = create(:tag, primary_name: 'AbC')
locale
expect(tag.reload.name('ain')).to eq("Tag_##{ tag.id }")
expect(other.reload.name('ain')).to eq('AbC')
end
it 'uses the exact fallback for a transcription that fails name validation' do
tag = create(:tag, primary_name: 'base')
source = TagName.new(name: 'nico:invalid', language_code: 'ja')
expect(TagName.generate_name(locale, tag, source)).to eq("Tag_##{ tag.id }")
end
it 'preserves human confirmation and is idempotent across repeated completion' do
locale
tag = Tag.find_or_create_by_tag_name!(Locale.nipponese, 'あいぬ', category: :general)
primary = tag.tag_name('ain')
primary.update!(name: 'human_confirmed', auto_generated: false)
snapshot = tag.tag_names.order(:id).map(&:attributes)
2.times { described_class.for_tag!(tag) }
expect(tag.tag_names.order(:id).map(&:attributes)).to eq(snapshot)
end
it 'creates one primary per active language through the production creation path' do
locale
tag = Tag.find_or_create_by_tag_name!(Locale.nipponese, 'アイヌ', category: :general)
expect(tag.tag_names.where(primary_flg: true).pluck(:language_code, :name))
.to contain_exactly(['ja', 'アイヌ'], ['ain', 'アイヌ'])
expect(Tag.find_or_create_by_tag_name!(locale, 'アイヌ', category: :general)).to eq(tag)
end
end
+34
ファイルの表示
@@ -0,0 +1,34 @@
require 'rails_helper'
RSpec.describe TagNameTranscription do
{
'AbCdXYZ' => 'AbCdXYZ',
'あいぬ' => 'アイヌ',
'アイヌ' => 'アイヌ',
'日本語' => 'ニチホンコ',
'漢字' => 'カンチ',
'漢あカ' => 'カン_ア_カ',
'漢字あいうカナ' => 'カンチ_アイウ_カナ',
'Ab漢あカZ' => 'Abカン_ア_カZ',
'りゃリュリョ' => 'リヤ_リユリヨ',
'キャット' => 'キヤット',
'コーヒー' => 'コヒ',
'コウコオケイケエカア' => 'コウコオケイケエカア',
'ガギグゲゴダヂヅデド' => 'カキクケコタチチュテト',
'バビブベボパピプペポ' => 'パピプペポパピプペポ',
'ザジズゼゾ' => 'チャチチュチェチョ',
'ジャジュジョ' => 'チヤチユチヨ',
'アㇰアㇱアッアンアㇺアㇷ゚' => 'アㇰアㇱアッアンアㇺアㇷ゚',
'アㇻイㇼウㇽエㇾオㇿ' => 'アㇻイㇼウㇽエㇾオㇿ',
'漢Aあ' => 'カンAア' }.each do |source, expected|
it "transcribes #{ source } as #{ expected }" do
expect(described_class.ainu(source)).to eq(expected)
end
end
['😀', 'ヴ', 'ティ', 'ㇵ', '魑', '𠮷', 'ー', '々'].each do |source|
it "rejects unsupported input #{ source }" do
expect { described_class.ainu(source) }.to raise_error(described_class::Unconvertible)
end
end
end
+14 -17
ファイルの表示
@@ -1,7 +1,7 @@
require 'rails_helper'
RSpec.describe TagVersionRecorder do
include_context 'English locale'
include_context 'Ainu locale'
let(:user) { create_member_user! }
let!(:tag) { create(:tag, primary_name: '日本語名') }
@@ -17,11 +17,11 @@ RSpec.describe TagVersionRecorder do
{ 'name' => 'alias_z', 'script_code' => 'Jpan' }] }
end
def prepare_english_names!
def prepare_ainu_names!
locale
primary_tag_name_for(tag, 'en').update!(name: 'English name', auto_generated: false)
create(:tag_name, :alias, tag:, language_code: 'en', script_code: 'Latn',
name: 'English alias')
primary_tag_name_for(tag, 'ain').update!(name: 'Ainu name', auto_generated: false)
create(:tag_name, :alias, tag:, language_code: 'ain', script_code: 'Kana',
name: 'Ainu alias')
end
it 'snapshots the Nipponese primary and sorted aliases in grouped names' do
@@ -62,35 +62,32 @@ RSpec.describe TagVersionRecorder do
end
it 'snapshots future languages together without a request locale argument' do
pending '日本語以外の Locale 有効化は別課題で実装する'
prepare_english_names!
prepare_ainu_names!
version = described_class.record!(tag:, event_type: :create, created_by_user: user)
expect(version.names).to eq(
'ja' => nipponese_snapshot,
'en' => { 'name' => 'English name', 'script_code' => 'Latn',
'ain' => { 'name' => 'Ainu name', 'script_code' => 'Kana',
'auto_generated' => false,
'aliases' => [{ 'name' => 'English alias', 'script_code' => 'Latn' }] })
'aliases' => [{ 'name' => 'Ainu alias', 'script_code' => 'Kana' }] })
end
it 'creates a version for an English-only change and preserves prior names' do
pending '日本語以外の Locale 有効化は別課題で実装する'
prepare_english_names!
it 'creates a version for an Ainu-only change and preserves prior names' do
prepare_ainu_names!
original = described_class.record!(tag:, event_type: :create, created_by_user: user)
primary_tag_name_for(tag, 'en').update!(name: 'Renamed')
primary_tag_name_for(tag, 'ain').update!(name: 'Renamed')
updated = described_class.record!(tag:, event_type: :update, created_by_user: user)
expect(updated.version_no).to eq(2)
expect(original.reload.names.fetch('en')).to include('name' => 'English name')
expect(updated.names.fetch('en')).to include('name' => 'Renamed')
expect(original.reload.names.fetch('ain')).to include('name' => 'Ainu name')
expect(updated.names.fetch('ain')).to include('name' => 'Renamed')
expect(updated.names.fetch('ja')).to eq(nipponese_snapshot)
end
it 'does not create a version for an unchanged multilingual snapshot' do
pending '日本語以外の Locale 有効化は別課題で実装する'
prepare_english_names!
prepare_ainu_names!
described_class.record!(tag:, event_type: :create, created_by_user: user)
unchanged = described_class.record!(tag:, event_type: :update, created_by_user: user)
+7 -4
ファイルの表示
@@ -1,19 +1,22 @@
RSpec.shared_context 'Ainu locale contract' do
let(:ainu_locale) { instance_double(Locale, language_code: 'ain', script_code: 'Jpan') }
let(:ainu_locale) do
instance_double(Locale, language_code: 'ain', script_code: 'Kana', deprecated_at: nil)
end
before do
# Only the foreign-key reference is seeded; no Locale or enum is enabled.
# Keep description contracts independent of locale activation and backfill.
Language.connection.execute(<<~SQL)
INSERT IGNORE INTO languages (code, name, created_at)
VALUES ('ain', 'Ainu', CURRENT_TIMESTAMP)
SQL
Script.find_or_create_by!(code: 'Kana') { _1.name = 'Kana' }
allow(Locale).to receive(:find_by).and_call_original
allow(Locale).to receive(:find_by).with(code: 'ain').and_return(ainu_locale)
allow(Locale).to receive(:find_by).with(code: 'ain-Kana').and_return(ainu_locale)
end
def add_ainu_name tag, name
TagName.insert_all!([{
tag_id: tag.id, language_code: 'ain', script_code: 'Jpan', name:,
tag_id: tag.id, language_code: 'ain', script_code: 'Kana', name:,
primary_flg: true, auto_generated: false,
created_at: Time.current, updated_at: Time.current }])
tag.tag_names.reset
+10 -15
ファイルの表示
@@ -1,22 +1,17 @@
RSpec.shared_context 'English locale' do
def prepare_english_reference!
Language.find_or_create_by!(code: 'en') { _1.name = 'English' }
Script.find_or_create_by!(code: 'Latn') { _1.name = 'Latin' }
region_class = Class.new(ApplicationRecord) { self.table_name = 'regions' }
region_class.find_or_create_by!(code: 826) do |record|
record.alpha_code = 'GB'
record.name = 'United Kingdom'
end
RSpec.shared_context 'Ainu locale' do
def prepare_ainu_reference!
Language.find_or_create_by!(code: 'ain') { _1.name = 'ain' }
Script.find_or_create_by!(code: 'Kana') { _1.name = 'Kana' }
end
let(:locale) do
prepare_english_reference!
Locale.find_or_create_by!(code: 'en') do |record|
record.language_code = 'en'
record.script_code = 'Latn'
record.region_code = 826
prepare_ainu_reference!
Locale.find_or_create_by!(code: 'ain-Kana') do |record|
record.language_code = 'ain'
record.script_code = 'Kana'
record.region_code = 392
record.variants = ''
record.name = 'English'
record.name = 'Ainu'
end
end
end