purizumu 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/purizumu/model_extensions.rb +24 -1
- data/lib/purizumu/translator.rb +1 -0
- data/lib/purizumu/version.rb +1 -1
- data/spec/purizumu/translator_spec.rb +169 -2
- data/spec/spec_helper.rb +2 -0
- metadata +2 -2
- /data/spec/tmp/generator/db/migrate/{20260412193408_create_purizumu_translations.rb → 20260413083431_create_purizumu_translations.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 13e13e8ac2807b2872602bfc6066e18bb4b9c462c3abb09a4d9dc3922ffa93a8
|
|
4
|
+
data.tar.gz: 68f318bb8f0bfd86d86cf7e2f5490bcf1d47cc9c804ee8edb83d2c8c0f56b501
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 818aaf90a7292954a157faa2787d17b1c9dfa643a865d23c7375c211d6a2f10a95e5fddf41f2c8e820b96c9abc1b6e98412873e183b96fe89bc9baf5da0154c2
|
|
7
|
+
data.tar.gz: 4a1784db93876921e7d32ea8f1db7ba9276017ce27f360aa081ce4aff4632f7e07d23f30424d36c7dc3db9489497ee3fda58b994ae7ccebc3e40c2b5473e2637
|
|
@@ -5,17 +5,40 @@ module Purizumu
|
|
|
5
5
|
module ModelExtensions
|
|
6
6
|
def self.included(base)
|
|
7
7
|
base.extend(ClassMethods)
|
|
8
|
+
base.class_attribute :purizumu_translated_attributes, instance_accessor: false, default: []
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
# Defines translated attribute readers on Active Record models.
|
|
11
12
|
module ClassMethods
|
|
12
13
|
def attribute_translation(*attribute_names)
|
|
13
|
-
attribute_names.flatten.
|
|
14
|
+
translated_attribute_names = attribute_names.flatten.map(&:to_s)
|
|
15
|
+
self.purizumu_translated_attributes = (
|
|
16
|
+
purizumu_translated_attributes + translated_attribute_names
|
|
17
|
+
).uniq
|
|
18
|
+
|
|
19
|
+
after_commit :purizumu_clear_translations_after_commit, on: :update
|
|
20
|
+
|
|
21
|
+
translated_attribute_names.each do |attribute_name|
|
|
14
22
|
define_method(attribute_name) do
|
|
15
23
|
Purizumu.translate_attribute(self, attribute_name)
|
|
16
24
|
end
|
|
17
25
|
end
|
|
18
26
|
end
|
|
19
27
|
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def purizumu_clear_translations_after_commit
|
|
32
|
+
changed_translated_attributes = (
|
|
33
|
+
previous_changes.keys & self.class.purizumu_translated_attributes
|
|
34
|
+
)
|
|
35
|
+
return if changed_translated_attributes.empty?
|
|
36
|
+
|
|
37
|
+
Purizumu::Translation.where(
|
|
38
|
+
model_class_name: self.class.name,
|
|
39
|
+
record_id: id,
|
|
40
|
+
attribute_name: changed_translated_attributes
|
|
41
|
+
).delete_all
|
|
42
|
+
end
|
|
20
43
|
end
|
|
21
44
|
end
|
data/lib/purizumu/translator.rb
CHANGED
data/lib/purizumu/version.rb
CHANGED
|
@@ -9,11 +9,11 @@ RSpec.describe Purizumu::Translator do
|
|
|
9
9
|
stub_const('GemRecord', Class.new(ActiveRecord::Base) do
|
|
10
10
|
self.table_name = 'gems'
|
|
11
11
|
|
|
12
|
-
attribute_translation :human_name
|
|
12
|
+
attribute_translation :human_name, :tagline
|
|
13
13
|
end)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
let!(:record) { GemRecord.create!(human_name: 'Prism') }
|
|
16
|
+
let!(:record) { GemRecord.create!(human_name: 'Prism', tagline: 'Crystal clear', slug: 'prism') }
|
|
17
17
|
let(:translation_scope) do
|
|
18
18
|
Purizumu::Translation.where(
|
|
19
19
|
model_class_name: 'GemRecord',
|
|
@@ -109,12 +109,171 @@ RSpec.describe Purizumu::Translator do
|
|
|
109
109
|
|
|
110
110
|
expect(Purizumu::Translation.where(record_id: blank_record.id)).to be_empty
|
|
111
111
|
end
|
|
112
|
+
|
|
113
|
+
context 'with an unsaved record' do
|
|
114
|
+
let(:unsaved_record) { GemRecord.new(human_name: 'Prism', slug: 'prism') }
|
|
115
|
+
|
|
116
|
+
before do
|
|
117
|
+
allow(Purizumu).to receive(:engine)
|
|
118
|
+
I18n.locale = :jp
|
|
119
|
+
unsaved_record.human_name
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'returns the source content' do
|
|
123
|
+
expect(unsaved_record.human_name).to eq('Prism')
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'does not invoke the engine' do
|
|
127
|
+
expect(Purizumu).not_to have_received(:engine)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'does not create a translation row' do
|
|
131
|
+
expect(Purizumu::Translation.where(model_class_name: 'GemRecord')).to be_empty
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
context 'when validations read a translated attribute on create' do
|
|
136
|
+
before do
|
|
137
|
+
stub_validated_gem_record
|
|
138
|
+
allow(Purizumu).to receive(:engine)
|
|
139
|
+
I18n.locale = :jp
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'does not raise during create' do
|
|
143
|
+
expect { create_validated_gem_record }.not_to raise_error
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'does not invoke the engine' do
|
|
147
|
+
create_validated_gem_record
|
|
148
|
+
|
|
149
|
+
expect(Purizumu).not_to have_received(:engine)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it 'does not create translation rows' do
|
|
153
|
+
create_validated_gem_record
|
|
154
|
+
|
|
155
|
+
expect(Purizumu::Translation.where(model_class_name: 'ValidatedGemRecord')).to be_empty
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
context 'when callbacks read a translated attribute before validation on create' do
|
|
160
|
+
before do
|
|
161
|
+
stub_slugged_gem_record
|
|
162
|
+
allow(Purizumu).to receive(:engine)
|
|
163
|
+
I18n.locale = :jp
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it 'allows the callback to derive values from the source attribute' do
|
|
167
|
+
expect(create_slugged_gem_record.slug).to eq('crystal-clear')
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'does not invoke the engine' do
|
|
171
|
+
create_slugged_gem_record
|
|
172
|
+
|
|
173
|
+
expect(Purizumu).not_to have_received(:engine)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it 'does not create translation rows' do
|
|
177
|
+
create_slugged_gem_record
|
|
178
|
+
|
|
179
|
+
expect(Purizumu::Translation.where(model_class_name: 'SluggedGemRecord')).to be_empty
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'clears persisted translations for a changed translated attribute after commit' do
|
|
184
|
+
create_existing_translation
|
|
185
|
+
|
|
186
|
+
expect { record.update!(human_name: 'Politics') }
|
|
187
|
+
.to change { translation_scope.pluck(:attribute_name, :locale, :content) }
|
|
188
|
+
.from([%w[human_name jp プリズム]]).to([])
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it 'preserves other translated attribute rows when one translated attribute changes' do
|
|
192
|
+
create_existing_translation
|
|
193
|
+
create_tagline_translation
|
|
194
|
+
record.update!(human_name: 'Politics')
|
|
195
|
+
|
|
196
|
+
expect(tagline_translation_rows).to eq([%w[tagline jp とても明快]])
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it 'regenerates translations from the new source content after invalidation' do
|
|
200
|
+
prepare_regenerated_translation
|
|
201
|
+
|
|
202
|
+
expect(record.human_name).to eq('Политика')
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it 'persists regenerated translations after invalidation' do
|
|
206
|
+
prepare_regenerated_translation
|
|
207
|
+
record.human_name
|
|
208
|
+
|
|
209
|
+
expect(
|
|
210
|
+
translation_scope.find_by!(locale: 'ru').content
|
|
211
|
+
).to eq('Политика')
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it 'does not clear translations when only an untranslated attribute changes' do
|
|
215
|
+
create_existing_translation
|
|
216
|
+
|
|
217
|
+
expect do
|
|
218
|
+
record.update!(slug: 'renamed-prism')
|
|
219
|
+
end.not_to(change { translation_scope.pluck(:attribute_name, :locale, :content) })
|
|
220
|
+
end
|
|
112
221
|
end
|
|
113
222
|
|
|
114
223
|
def create_existing_translation
|
|
115
224
|
Purizumu::Translation.create!(existing_translation_attributes)
|
|
116
225
|
end
|
|
117
226
|
|
|
227
|
+
def stub_validated_gem_record
|
|
228
|
+
stub_const('ValidatedGemRecord', Class.new(ActiveRecord::Base) do
|
|
229
|
+
self.table_name = 'gems'
|
|
230
|
+
|
|
231
|
+
validates :human_name, presence: true
|
|
232
|
+
attribute_translation :human_name
|
|
233
|
+
end)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def create_validated_gem_record
|
|
237
|
+
ValidatedGemRecord.create!(human_name: 'Prism', slug: 'prism')
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def stub_slugged_gem_record
|
|
241
|
+
stub_const('SluggedGemRecord', Class.new(ActiveRecord::Base) do
|
|
242
|
+
self.table_name = 'gems'
|
|
243
|
+
|
|
244
|
+
validates :human_name, presence: true
|
|
245
|
+
before_validation :generate_slug, if: -> { slug.blank? }
|
|
246
|
+
attribute_translation :human_name
|
|
247
|
+
|
|
248
|
+
def generate_slug
|
|
249
|
+
self.slug = human_name.to_s.parameterize
|
|
250
|
+
end
|
|
251
|
+
end)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def create_slugged_gem_record
|
|
255
|
+
SluggedGemRecord.create!(human_name: 'Crystal clear')
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def prepare_regenerated_translation
|
|
259
|
+
create_existing_translation
|
|
260
|
+
allow(Purizumu).to receive(:engine).and_return(
|
|
261
|
+
instance_double(Purizumu::Engines::Xai, translate: 'Политика')
|
|
262
|
+
)
|
|
263
|
+
record.update!(human_name: 'Politics')
|
|
264
|
+
I18n.locale = :ru
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def create_tagline_translation
|
|
268
|
+
Purizumu::Translation.create!(
|
|
269
|
+
model_class_name: 'GemRecord',
|
|
270
|
+
record_id: record.id,
|
|
271
|
+
attribute_name: 'tagline',
|
|
272
|
+
locale: 'jp',
|
|
273
|
+
content: 'とても明快'
|
|
274
|
+
)
|
|
275
|
+
end
|
|
276
|
+
|
|
118
277
|
def expected_engine_arguments
|
|
119
278
|
{
|
|
120
279
|
model_name: 'GemRecord',
|
|
@@ -124,4 +283,12 @@ RSpec.describe Purizumu::Translator do
|
|
|
124
283
|
source_locale: 'en'
|
|
125
284
|
}
|
|
126
285
|
end
|
|
286
|
+
|
|
287
|
+
def tagline_translation_rows
|
|
288
|
+
Purizumu::Translation.where(
|
|
289
|
+
model_class_name: 'GemRecord',
|
|
290
|
+
record_id: record.id,
|
|
291
|
+
attribute_name: 'tagline'
|
|
292
|
+
).pluck(:attribute_name, :locale, :content)
|
|
293
|
+
end
|
|
127
294
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: purizumu
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenAI Codex
|
|
@@ -79,7 +79,7 @@ files:
|
|
|
79
79
|
- spec/purizumu/generators/install_generator_spec.rb
|
|
80
80
|
- spec/purizumu/translator_spec.rb
|
|
81
81
|
- spec/spec_helper.rb
|
|
82
|
-
- spec/tmp/generator/db/migrate/
|
|
82
|
+
- spec/tmp/generator/db/migrate/20260413083431_create_purizumu_translations.rb
|
|
83
83
|
homepage: https://example.com/purizumu
|
|
84
84
|
licenses:
|
|
85
85
|
- MIT
|