copy_tuner_client 2.2.0 → 2.3.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/CHANGELOG.md +2 -235
- data/CLAUDE.md +10 -0
- data/README.md +42 -5
- data/docs/poller-startup.md +108 -0
- data/lib/copy_tuner_client/configuration.rb +37 -12
- data/lib/copy_tuner_client/fork_hook.rb +53 -0
- data/lib/copy_tuner_client/poller.rb +87 -23
- data/lib/copy_tuner_client/process_guard.rb +13 -4
- data/lib/copy_tuner_client/queue_with_timeout.rb +8 -2
- data/lib/copy_tuner_client/version.rb +1 -1
- data/skills/copy-tuner-to-locales-cleanup/SKILL.md +5 -5
- data/skills/copy-tuner-to-locales-migrate-prefix/SKILL.md +69 -37
- data/skills/copy-tuner-to-locales-migrate-prefix/references/example-touchpoints.md +31 -10
- data/skills/copy-tuner-to-locales-migrate-prefix/scripts/migrate_prefix.rb +144 -6
- data/spec/copy_tuner_client/configuration_spec.rb +173 -0
- data/spec/copy_tuner_client/fork_hook_spec.rb +171 -0
- data/spec/copy_tuner_client/poller_spec.rb +148 -1
- data/spec/copy_tuner_client/process_guard_spec.rb +8 -0
- data/spec/copy_tuner_client/queue_with_timeout_spec.rb +27 -0
- data/spec/copy_tuner_client/warden_integration_spec.rb +95 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/middleware_stack.rb +39 -5
- metadata +6 -1
|
@@ -20,6 +20,14 @@ describe CopyTunerClient::ProcessGuard do
|
|
|
20
20
|
process_guard
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
it 'fork の前後で poller を張り直すフックを登録する' do
|
|
24
|
+
allow(CopyTunerClient::ForkHook).to receive(:install)
|
|
25
|
+
|
|
26
|
+
build_process_guard.start
|
|
27
|
+
|
|
28
|
+
expect(CopyTunerClient::ForkHook).to have_received(:install)
|
|
29
|
+
end
|
|
30
|
+
|
|
23
31
|
it 'starts polling from a worker process' do
|
|
24
32
|
process_guard = build_process_guard
|
|
25
33
|
process_guard.start
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'timeout'
|
|
3
|
+
|
|
4
|
+
describe CopyTunerClient::QueueWithTimeout do
|
|
5
|
+
subject(:queue) { described_class.new }
|
|
6
|
+
|
|
7
|
+
describe '#pop_with_timeout' do
|
|
8
|
+
it 'キューに要素があれば取り出す' do
|
|
9
|
+
queue << :sync
|
|
10
|
+
|
|
11
|
+
expect(queue.pop_with_timeout(0)).to eq(:sync)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it '要素が無いままタイムアウトしたら ThreadError を投げる' do
|
|
15
|
+
expect { queue.pop_with_timeout(0.1) }.to raise_error(ThreadError)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'ウォールクロックが巻き戻ってもタイムアウトが伸びない' do
|
|
19
|
+
# 待っている最中に NTP の step 補正や手動の時刻変更で時計が後ろへ飛ぶ状況を模す。
|
|
20
|
+
# 経過時間をウォールクロックで測っていると、飛んだ幅がそのまま待ち時間に乗る
|
|
21
|
+
base = Time.now
|
|
22
|
+
allow(Time).to receive(:now).and_return(base, base - 3600)
|
|
23
|
+
|
|
24
|
+
expect { Timeout.timeout(2) { queue.pop_with_timeout(0.1) } }.to raise_error(ThreadError)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'copy_tuner_client/copyray_middleware'
|
|
3
|
+
require 'copy_tuner_client/copyray'
|
|
4
|
+
require 'copy_tuner_client/copyray/marker'
|
|
5
|
+
require 'copy_tuner_client/translation_log'
|
|
6
|
+
|
|
7
|
+
# Warden::Manager の catch(:warden) と failure app 呼び出しだけを模した fake(warden gem に依存しないため)
|
|
8
|
+
class FakeWarden
|
|
9
|
+
def initialize(app, failure_app)
|
|
10
|
+
@app = app
|
|
11
|
+
@failure_app = failure_app
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(env)
|
|
15
|
+
result = catch(:warden) { @app.call(env) }
|
|
16
|
+
result.is_a?(Array) ? result : @failure_app.call(env)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# NOTE: CopyrayMiddleware 単体ではなく、Warden 相当の fake との組み合わせ位置を検証するため
|
|
21
|
+
# describe の第一引数はクラスではなく説明文にしている
|
|
22
|
+
describe 'throw :warden と CopyrayMiddleware の位置関係' do # rubocop:disable RSpec/DescribeClass
|
|
23
|
+
def marker(key)
|
|
24
|
+
CopyTunerClient::Copyray::Marker.encode(key)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# NOTE: authenticate_user! 失敗を模す。throw :warden するとこの @app.call(env) は正常リターンせず、
|
|
28
|
+
# CopyrayMiddleware が内側にあると Rewriter.rewrite が実行されない。
|
|
29
|
+
let(:throwing_app) { ->(_env) { throw :warden } }
|
|
30
|
+
let(:failure_status) { 200 }
|
|
31
|
+
let(:failure_app) do
|
|
32
|
+
->(_env) do
|
|
33
|
+
[failure_status, { 'Content-Type' => 'text/html' }, [failure_body]]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
let(:failure_body) { "<html><body><p>#{marker('devise.failure.unauthenticated')}Please sign in</p></body></html>" }
|
|
37
|
+
|
|
38
|
+
before do
|
|
39
|
+
CopyTunerClient.configure do |configuration|
|
|
40
|
+
configuration.project_id = 1
|
|
41
|
+
configuration.client = FakeClient.new
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# NOTE: append_js は Rails の ActionController::Base.helpers に依存するため、
|
|
46
|
+
# copyray_middleware_spec.rb と同じ手法で no-op スタブに差し替え、Rewriter の効果だけを見る。
|
|
47
|
+
def stub_append_js(middleware)
|
|
48
|
+
allow(middleware).to receive(:append_js) { |html, *| html }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context '内側構成(CopyrayMiddleware が Warden より内側)のとき' do
|
|
52
|
+
it 'マーカー ⟦CT: がレスポンスに残る(不具合の記録)' do
|
|
53
|
+
copyray = CopyTunerClient::CopyrayMiddleware.new(throwing_app)
|
|
54
|
+
stub_append_js(copyray)
|
|
55
|
+
warden = FakeWarden.new(copyray, failure_app)
|
|
56
|
+
|
|
57
|
+
_status, _headers, response = warden.call({})
|
|
58
|
+
result = response.join
|
|
59
|
+
|
|
60
|
+
expect(result).to match(CopyTunerClient::Copyray::Marker::SCAN_REGEXP)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context '外側構成(CopyrayMiddleware が Warden より外側)のとき' do
|
|
65
|
+
it 'マーカーが消え data-copyray-key に変換される' do
|
|
66
|
+
warden = FakeWarden.new(throwing_app, failure_app)
|
|
67
|
+
copyray = CopyTunerClient::CopyrayMiddleware.new(warden)
|
|
68
|
+
stub_append_js(copyray)
|
|
69
|
+
|
|
70
|
+
_status, _headers, response = copyray.call({})
|
|
71
|
+
result = response.join
|
|
72
|
+
|
|
73
|
+
expect(result).to include('data-copyray-key="devise.failure.unauthenticated"')
|
|
74
|
+
expect(result).not_to match(CopyTunerClient::Copyray::Marker::SCAN_REGEXP)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context 'failure app が 422 を返すとき(Devise.responder.error_status = :unprocessable_entity 相当)' do
|
|
78
|
+
let(:unprocessable_failure_app) do
|
|
79
|
+
->(_env) { [422, { 'Content-Type' => 'text/html' }, [failure_body]] }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'マーカーが消え data-copyray-key に変換される' do
|
|
83
|
+
warden = FakeWarden.new(throwing_app, unprocessable_failure_app)
|
|
84
|
+
copyray = CopyTunerClient::CopyrayMiddleware.new(warden)
|
|
85
|
+
stub_append_js(copyray)
|
|
86
|
+
|
|
87
|
+
_status, _headers, response = copyray.call({})
|
|
88
|
+
result = response.join
|
|
89
|
+
|
|
90
|
+
expect(result).to include('data-copyray-key="devise.failure.unauthenticated"')
|
|
91
|
+
expect(result).not_to match(CopyTunerClient::Copyray::Marker::SCAN_REGEXP)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -26,4 +26,10 @@ RSpec.configure do |config|
|
|
|
26
26
|
FakeCopyTunerApp.reset
|
|
27
27
|
reset_config
|
|
28
28
|
end
|
|
29
|
+
|
|
30
|
+
# apply を通す spec は本物の poller スレッドを起動するので、example ごとに止める。
|
|
31
|
+
# 放置するとスレッドがスイート終了まで積み上がり、実際に HTTP を叩き続ける
|
|
32
|
+
config.after do
|
|
33
|
+
CopyTunerClient.configuration&.poller&.stop
|
|
34
|
+
end
|
|
29
35
|
end
|
|
@@ -1,13 +1,47 @@
|
|
|
1
|
+
# ActionDispatch::MiddlewareStack の挿入 API を模した fake(挿入順序と引数を検証するため)
|
|
1
2
|
class MiddlewareStack
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
Entry = Struct.new(:klass, :args)
|
|
4
|
+
|
|
5
|
+
def initialize(existing = [])
|
|
6
|
+
@middlewares = existing.map { |klass| Entry.new(klass, []) }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def use(klass, *args)
|
|
10
|
+
@middlewares << Entry.new(klass, args)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def insert_before(target, klass, *args)
|
|
14
|
+
index = index_of!(target, 'before')
|
|
15
|
+
@middlewares.insert(index, Entry.new(klass, args))
|
|
4
16
|
end
|
|
5
17
|
|
|
6
|
-
def
|
|
7
|
-
|
|
18
|
+
def insert_after(target, klass, *args)
|
|
19
|
+
index = index_of!(target, 'after')
|
|
20
|
+
@middlewares.insert(index + 1, Entry.new(klass, args))
|
|
8
21
|
end
|
|
9
22
|
|
|
10
23
|
def include?(klass)
|
|
11
|
-
|
|
24
|
+
classes.include?(klass)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def classes
|
|
28
|
+
@middlewares.map(&:klass)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def index(klass)
|
|
32
|
+
classes.index(klass)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def args_for(klass)
|
|
36
|
+
@middlewares.find { |entry| entry.klass == klass }&.args
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def index_of!(target, where)
|
|
42
|
+
index = classes.index(target)
|
|
43
|
+
raise "No such middleware to insert #{where}: #{target.inspect}" if index.nil?
|
|
44
|
+
|
|
45
|
+
index
|
|
12
46
|
end
|
|
13
47
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: copy_tuner_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SonicGarden
|
|
@@ -200,6 +200,7 @@ files:
|
|
|
200
200
|
- app/assets/javascripts/copytuner.js
|
|
201
201
|
- biome.json
|
|
202
202
|
- copy_tuner_client.gemspec
|
|
203
|
+
- docs/poller-startup.md
|
|
203
204
|
- gemfiles/8.0.gemfile
|
|
204
205
|
- gemfiles/8.1.gemfile
|
|
205
206
|
- gemfiles/main.gemfile
|
|
@@ -215,6 +216,7 @@ files:
|
|
|
215
216
|
- lib/copy_tuner_client/dotted_hash.rb
|
|
216
217
|
- lib/copy_tuner_client/engine.rb
|
|
217
218
|
- lib/copy_tuner_client/errors.rb
|
|
219
|
+
- lib/copy_tuner_client/fork_hook.rb
|
|
218
220
|
- lib/copy_tuner_client/helper_extension.rb
|
|
219
221
|
- lib/copy_tuner_client/i18n_backend.rb
|
|
220
222
|
- lib/copy_tuner_client/i18n_compat.rb
|
|
@@ -251,13 +253,16 @@ files:
|
|
|
251
253
|
- spec/copy_tuner_client/copyray_middleware_spec.rb
|
|
252
254
|
- spec/copy_tuner_client/copyray_spec.rb
|
|
253
255
|
- spec/copy_tuner_client/dotted_hash_spec.rb
|
|
256
|
+
- spec/copy_tuner_client/fork_hook_spec.rb
|
|
254
257
|
- spec/copy_tuner_client/helper_extension_spec.rb
|
|
255
258
|
- spec/copy_tuner_client/i18n_backend_spec.rb
|
|
256
259
|
- spec/copy_tuner_client/poller_spec.rb
|
|
257
260
|
- spec/copy_tuner_client/prefixed_logger_spec.rb
|
|
258
261
|
- spec/copy_tuner_client/process_guard_spec.rb
|
|
262
|
+
- spec/copy_tuner_client/queue_with_timeout_spec.rb
|
|
259
263
|
- spec/copy_tuner_client/request_sync_spec.rb
|
|
260
264
|
- spec/copy_tuner_client/translation_log_spec.rb
|
|
265
|
+
- spec/copy_tuner_client/warden_integration_spec.rb
|
|
261
266
|
- spec/copy_tuner_client_spec.rb
|
|
262
267
|
- spec/spec_helper.rb
|
|
263
268
|
- spec/support/client_spec_helpers.rb
|