purizumu 0.1.2 → 0.2.0
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/README.md +60 -1
- data/lib/purizumu/engines/test.rb +32 -0
- data/lib/purizumu/version.rb +1 -1
- data/lib/purizumu.rb +19 -0
- data/spec/purizumu/test_mode_spec.rb +165 -0
- metadata +4 -2
- /data/spec/tmp/generator/db/migrate/{20260413083431_create_purizumu_translations.rb → 20260814023453_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: 72573b19670b458b24a5da4260623d80470ddfc2948e197fc521264aa4825bc4
|
|
4
|
+
data.tar.gz: 2a06be3b4baea735405a4b37a6f22765507504cb1d049703420424249422852b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e2a338946fd43b0e867f29eb6863cda8b4732a3659dff4b698577735d0bf52b367517ce56f62523dd8106134def31634134c32bc5cf6c786c05b84aaab67175e
|
|
7
|
+
data.tar.gz: b10400ba178ce055773d6c602039ff09be5ff85821645b5e1aa320dde7d473c79fb5a7b81bf0067f4508fc77b3d6b811743cd0b11f1793865cbdcb55d5690d21
|
data/README.md
CHANGED
|
@@ -43,7 +43,7 @@ end
|
|
|
43
43
|
|
|
44
44
|
Available configuration options:
|
|
45
45
|
|
|
46
|
-
- `config.engine`: translation engine symbol. Purizumu currently ships with `:xai
|
|
46
|
+
- `config.engine`: translation engine symbol. Purizumu currently ships with `:xai` and `:test` (see [Test Mode](#test-mode)).
|
|
47
47
|
- `config.key`: API key for the configured engine.
|
|
48
48
|
- `config.model`: LLM model name.
|
|
49
49
|
- `config.base_url`: API base URL. Defaults to `https://api.x.ai/v1`.
|
|
@@ -130,6 +130,65 @@ The expected tool payload is:
|
|
|
130
130
|
}
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
## Test Mode
|
|
134
|
+
|
|
135
|
+
In a test suite you usually do not want translated attribute reads to hit the configured translation engine's API. Enable test mode and Purizumu will stub translations locally without any network requests:
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
Purizumu.test_mode!
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
While test mode is on, `Purizumu.engine` returns a stub engine regardless of the configured engine, and no API key or model is required. By default, the stub returns `"[<locale>] <original content>"`, so `"Prism"` translated to `:jp` becomes `"[jp] Prism"` — deterministic and easy to assert against.
|
|
142
|
+
|
|
143
|
+
You can also supply a block to control what the stub returns. The block receives the full translation request as keyword arguments (`model_name`, `attribute_name`, `content`, `locale`, `source_locale`):
|
|
144
|
+
|
|
145
|
+
```ruby
|
|
146
|
+
Purizumu.test_mode! do |content:, locale:, **|
|
|
147
|
+
"#{content} (#{locale})"
|
|
148
|
+
end
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
To turn it back off (and clear any custom block):
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
Purizumu.disable_test_mode!
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
You can check the current state with `Purizumu.test_mode?`.
|
|
158
|
+
|
|
159
|
+
### RSpec
|
|
160
|
+
|
|
161
|
+
Enable it once for the entire run in `spec/spec_helper.rb` (or `rails_helper.rb`):
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
RSpec.configure do |config|
|
|
165
|
+
config.before(:suite) do
|
|
166
|
+
Purizumu.test_mode!
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Or, if you only want it for specific examples, enable it per test:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
before { Purizumu.test_mode! }
|
|
175
|
+
after { Purizumu.disable_test_mode! }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Test mode is independent of configuration, so it survives `Purizumu.reset_configuration!` — enabling it in `before(:suite)` keeps it on even if individual tests reset or rebuild the configuration.
|
|
179
|
+
|
|
180
|
+
### Configuring the Test Engine Directly
|
|
181
|
+
|
|
182
|
+
If you prefer plain configuration — for example in a Rails `config/environments/test.rb` setup where nothing resets Purizumu's configuration — the stub engine is also available as a regular engine:
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
Purizumu.configure do |config|
|
|
186
|
+
config.engine = :test
|
|
187
|
+
end
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
This behaves like test mode with no block: no network requests, no API key or model required, and the same `"[<locale>] <original content>"` stub output. The difference is that it lives in the configuration, so `Purizumu.reset_configuration!` or a later `Purizumu.configure` will replace it. `Purizumu.test_mode!` still overrides whatever engine is configured.
|
|
191
|
+
|
|
133
192
|
## Generator Output
|
|
134
193
|
|
|
135
194
|
`rails generate purizumu:install` creates a migration for the translation table with:
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Purizumu
|
|
4
|
+
module Engines
|
|
5
|
+
# Translation engine used in test mode. It never performs network
|
|
6
|
+
# requests; it returns a deterministic stub translation, or the result
|
|
7
|
+
# of the handler given to Purizumu.test_mode!.
|
|
8
|
+
class Test
|
|
9
|
+
def initialize(handler: nil)
|
|
10
|
+
@handler = handler
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def translate(model_name:, attribute_name:, content:, locale:, source_locale:)
|
|
14
|
+
if handler
|
|
15
|
+
handler.call(
|
|
16
|
+
model_name:,
|
|
17
|
+
attribute_name:,
|
|
18
|
+
content:,
|
|
19
|
+
locale:,
|
|
20
|
+
source_locale:
|
|
21
|
+
).to_s
|
|
22
|
+
else
|
|
23
|
+
"[#{locale}] #{content}"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
attr_reader :handler
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/purizumu/version.rb
CHANGED
data/lib/purizumu.rb
CHANGED
|
@@ -9,6 +9,7 @@ require 'uri'
|
|
|
9
9
|
require 'purizumu/version'
|
|
10
10
|
require 'purizumu/errors'
|
|
11
11
|
require 'purizumu/configuration'
|
|
12
|
+
require 'purizumu/engines/test'
|
|
12
13
|
require 'purizumu/engines/xai'
|
|
13
14
|
require 'purizumu/engines/xai/payload_builder'
|
|
14
15
|
require 'purizumu/engines/xai/response_parser'
|
|
@@ -32,16 +33,34 @@ module Purizumu
|
|
|
32
33
|
@configuration = Configuration.new
|
|
33
34
|
end
|
|
34
35
|
|
|
36
|
+
def test_mode!(&handler)
|
|
37
|
+
@test_mode = true
|
|
38
|
+
@test_handler = handler
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_mode?
|
|
42
|
+
@test_mode == true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def disable_test_mode!
|
|
46
|
+
@test_mode = false
|
|
47
|
+
@test_handler = nil
|
|
48
|
+
end
|
|
49
|
+
|
|
35
50
|
def translate_attribute(record, attribute_name, locale: I18n.locale)
|
|
36
51
|
Translator.new(record: record, attribute_name: attribute_name, locale: locale).call
|
|
37
52
|
end
|
|
38
53
|
|
|
39
54
|
def engine
|
|
55
|
+
return Engines::Test.new(handler: @test_handler) if test_mode?
|
|
56
|
+
|
|
40
57
|
engine_name = configuration.engine.to_sym
|
|
41
58
|
|
|
42
59
|
case engine_name
|
|
43
60
|
when :xai
|
|
44
61
|
Engines::Xai.new(configuration: configuration)
|
|
62
|
+
when :test
|
|
63
|
+
Engines::Test.new
|
|
45
64
|
else
|
|
46
65
|
raise UnsupportedEngineError, "Unsupported translation engine: #{engine_name}"
|
|
47
66
|
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
RSpec.describe Purizumu, '.test_mode!' do
|
|
6
|
+
after do
|
|
7
|
+
described_class.disable_test_mode!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'is disabled by default' do
|
|
11
|
+
expect(described_class.test_mode?).to be(false)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'enables test mode' do
|
|
15
|
+
described_class.test_mode!
|
|
16
|
+
|
|
17
|
+
expect(described_class.test_mode?).to be(true)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'returns the test engine while enabled' do
|
|
21
|
+
described_class.test_mode!
|
|
22
|
+
|
|
23
|
+
expect(described_class.engine).to be_a(Purizumu::Engines::Test)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'does not require engine configuration while enabled' do
|
|
27
|
+
described_class.test_mode!
|
|
28
|
+
|
|
29
|
+
expect(described_class.engine.translate(**translate_arguments)).to be_a(String)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'survives configuration resets' do
|
|
33
|
+
described_class.test_mode!
|
|
34
|
+
described_class.reset_configuration!
|
|
35
|
+
|
|
36
|
+
expect(described_class.test_mode?).to be(true)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'returns a deterministic stub translation by default' do
|
|
40
|
+
described_class.test_mode!
|
|
41
|
+
|
|
42
|
+
expect(described_class.engine.translate(**translate_arguments)).to eq('[jp] Prism')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'uses the handler result when a block is given' do
|
|
46
|
+
described_class.test_mode! { |locale:, content:, **| "#{content} in #{locale}" }
|
|
47
|
+
|
|
48
|
+
expect(described_class.engine.translate(**translate_arguments)).to eq('Prism in jp')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'passes the full request to the handler' do
|
|
52
|
+
seen = nil
|
|
53
|
+
described_class.test_mode! { |**request| seen = request }
|
|
54
|
+
described_class.engine.translate(**translate_arguments)
|
|
55
|
+
|
|
56
|
+
expect(seen).to eq(translate_arguments)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'coerces handler results to strings' do
|
|
60
|
+
described_class.test_mode! { |**| :symbolic }
|
|
61
|
+
|
|
62
|
+
expect(described_class.engine.translate(**translate_arguments)).to eq('symbolic')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'clears a previous handler when re-enabled without a block' do
|
|
66
|
+
described_class.test_mode! { |**| 'from handler' }
|
|
67
|
+
described_class.test_mode!
|
|
68
|
+
|
|
69
|
+
expect(described_class.engine.translate(**translate_arguments)).to eq('[jp] Prism')
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe '.disable_test_mode!' do
|
|
73
|
+
before do
|
|
74
|
+
described_class.test_mode! { |**| 'from handler' }
|
|
75
|
+
described_class.disable_test_mode!
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'disables test mode' do
|
|
79
|
+
expect(described_class.test_mode?).to be(false)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'restores the configured engine' do
|
|
83
|
+
expect(described_class.engine).to be_a(Purizumu::Engines::Xai)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'clears the handler for later re-enables' do
|
|
87
|
+
described_class.test_mode!
|
|
88
|
+
|
|
89
|
+
expect(described_class.engine.translate(**translate_arguments)).to eq('[jp] Prism')
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe 'configuring the test engine directly' do
|
|
94
|
+
before do
|
|
95
|
+
described_class.configure { |config| config.engine = :test }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'returns the test engine' do
|
|
99
|
+
expect(described_class.engine).to be_a(Purizumu::Engines::Test)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'returns the default stub translation' do
|
|
103
|
+
expect(described_class.engine.translate(**translate_arguments)).to eq('[jp] Prism')
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'is overridden by a test mode handler' do
|
|
107
|
+
described_class.test_mode! { |**| 'from handler' }
|
|
108
|
+
|
|
109
|
+
expect(described_class.engine.translate(**translate_arguments)).to eq('from handler')
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe 'translating model attributes in test mode' do
|
|
114
|
+
subject(:translated_name) { record.human_name }
|
|
115
|
+
|
|
116
|
+
before do
|
|
117
|
+
stub_const('GemRecord', Class.new(ActiveRecord::Base) do
|
|
118
|
+
self.table_name = 'gems'
|
|
119
|
+
|
|
120
|
+
attribute_translation :human_name
|
|
121
|
+
end)
|
|
122
|
+
|
|
123
|
+
described_class.test_mode!
|
|
124
|
+
I18n.locale = :jp
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
let!(:record) { GemRecord.create!(human_name: 'Prism') }
|
|
128
|
+
|
|
129
|
+
it 'returns the stubbed translation' do
|
|
130
|
+
expect(translated_name).to eq('[jp] Prism')
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'persists the stubbed translation' do
|
|
134
|
+
translated_name
|
|
135
|
+
|
|
136
|
+
expect(persisted_translation.content).to eq('[jp] Prism')
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'never opens an HTTP connection' do
|
|
140
|
+
allow(Net::HTTP).to receive(:new)
|
|
141
|
+
translated_name
|
|
142
|
+
|
|
143
|
+
expect(Net::HTTP).not_to have_received(:new)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def persisted_translation
|
|
148
|
+
Purizumu::Translation.find_by!(
|
|
149
|
+
model_class_name: 'GemRecord',
|
|
150
|
+
record_id: record.id,
|
|
151
|
+
attribute_name: 'human_name',
|
|
152
|
+
locale: 'jp'
|
|
153
|
+
)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def translate_arguments
|
|
157
|
+
{
|
|
158
|
+
model_name: 'GemRecord',
|
|
159
|
+
attribute_name: 'human_name',
|
|
160
|
+
content: 'Prism',
|
|
161
|
+
locale: 'jp',
|
|
162
|
+
source_locale: 'en'
|
|
163
|
+
}
|
|
164
|
+
end
|
|
165
|
+
end
|
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.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OpenAI Codex
|
|
@@ -65,6 +65,7 @@ files:
|
|
|
65
65
|
- lib/generators/purizumu/install/templates/create_purizumu_translations.rb.erb
|
|
66
66
|
- lib/purizumu.rb
|
|
67
67
|
- lib/purizumu/configuration.rb
|
|
68
|
+
- lib/purizumu/engines/test.rb
|
|
68
69
|
- lib/purizumu/engines/xai.rb
|
|
69
70
|
- lib/purizumu/engines/xai/payload_builder.rb
|
|
70
71
|
- lib/purizumu/engines/xai/response_parser.rb
|
|
@@ -77,9 +78,10 @@ files:
|
|
|
77
78
|
- spec/purizumu/configuration_spec.rb
|
|
78
79
|
- spec/purizumu/engines/xai_spec.rb
|
|
79
80
|
- spec/purizumu/generators/install_generator_spec.rb
|
|
81
|
+
- spec/purizumu/test_mode_spec.rb
|
|
80
82
|
- spec/purizumu/translator_spec.rb
|
|
81
83
|
- spec/spec_helper.rb
|
|
82
|
-
- spec/tmp/generator/db/migrate/
|
|
84
|
+
- spec/tmp/generator/db/migrate/20260814023453_create_purizumu_translations.rb
|
|
83
85
|
homepage: https://example.com/purizumu
|
|
84
86
|
licenses:
|
|
85
87
|
- MIT
|