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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13e13e8ac2807b2872602bfc6066e18bb4b9c462c3abb09a4d9dc3922ffa93a8
4
- data.tar.gz: 68f318bb8f0bfd86d86cf7e2f5490bcf1d47cc9c804ee8edb83d2c8c0f56b501
3
+ metadata.gz: 72573b19670b458b24a5da4260623d80470ddfc2948e197fc521264aa4825bc4
4
+ data.tar.gz: 2a06be3b4baea735405a4b37a6f22765507504cb1d049703420424249422852b
5
5
  SHA512:
6
- metadata.gz: 818aaf90a7292954a157faa2787d17b1c9dfa643a865d23c7375c211d6a2f10a95e5fddf41f2c8e820b96c9abc1b6e98412873e183b96fe89bc9baf5da0154c2
7
- data.tar.gz: 4a1784db93876921e7d32ea8f1db7ba9276017ce27f360aa081ce4aff4632f7e07d23f30424d36c7dc3db9489497ee3fda58b994ae7ccebc3e40c2b5473e2637
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Purizumu
4
- VERSION = '0.1.2'
4
+ VERSION = '0.2.0'
5
5
  end
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.1.2
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/20260413083431_create_purizumu_translations.rb
84
+ - spec/tmp/generator/db/migrate/20260814023453_create_purizumu_translations.rb
83
85
  homepage: https://example.com/purizumu
84
86
  licenses:
85
87
  - MIT