copy_tuner_client 2.4.0 → 2.5.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/lib/copy_tuner_client/copyray.rb +4 -0
- data/lib/copy_tuner_client/copyray_middleware.rb +2 -0
- data/lib/copy_tuner_client/engine.rb +7 -0
- data/lib/copy_tuner_client/helper_extension.rb +38 -4
- data/lib/copy_tuner_client/version.rb +1 -1
- data/spec/copy_tuner_client/copyray_middleware_spec.rb +14 -0
- data/spec/copy_tuner_client/helper_extension_spec.rb +101 -5
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 938a08aeb03719e98a6e118eb714b820798f91ff3d5e69cce65eeea3d8b243ca
|
|
4
|
+
data.tar.gz: 9da7bbe8fa7ed6b92b90fa090f9ebf5ec67cb8945bace845494aa7638640b3ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 86d05a06a42a06da4586d60bc80649cdb9f4cd98c26425d6528f7b121c2ca880b366bc12aeec7b20952bbc7129b2837dcd9261851bb960ddebd7de773e0b64b6
|
|
7
|
+
data.tar.gz: 831783835b4ede6b590dd298e89f596c8c9ca417260e4ae490f8b6bbca576e07c7b5f89a490eb8b2b651e305a66b0c9dac7c526f0592d2fdef7f7337d70f6607
|
|
@@ -3,6 +3,10 @@ require 'copy_tuner_client/copyray/marker'
|
|
|
3
3
|
module CopyTunerClient
|
|
4
4
|
# 翻訳テキストにオーバーレイ編集用のマーカートークンを埋め込む
|
|
5
5
|
class Copyray
|
|
6
|
+
# NOTE: マーカーは CopyrayMiddleware の Rewriter でしか除去されない。注入可否を「format が html か」ではなく
|
|
7
|
+
# 「この描画の出力が middleware を通るか」で判定するため、middleware が env にこの印を立てる。
|
|
8
|
+
ENV_KEY = 'copy_tuner.copyray_injectable'.freeze
|
|
9
|
+
|
|
6
10
|
# This:
|
|
7
11
|
# message
|
|
8
12
|
# Becomes:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# cf) xray-rails : xray/middleware.rb
|
|
2
2
|
|
|
3
|
+
require 'copy_tuner_client/copyray'
|
|
3
4
|
require 'copy_tuner_client/copyray/rewriter'
|
|
4
5
|
|
|
5
6
|
module CopyTunerClient
|
|
@@ -11,6 +12,7 @@ module CopyTunerClient
|
|
|
11
12
|
|
|
12
13
|
def call(env)
|
|
13
14
|
CopyTunerClient::TranslationLog.clear
|
|
15
|
+
env[CopyTunerClient::Copyray::ENV_KEY] = true
|
|
14
16
|
status, headers, response = @app.call(env)
|
|
15
17
|
if rewritable?(status, headers) && (body = response_body(response))
|
|
16
18
|
rewrite_response(env, status, headers, body, response)
|
|
@@ -17,6 +17,13 @@ module CopyTunerClient
|
|
|
17
17
|
)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
ActiveSupport.on_load(:action_controller_base) do
|
|
21
|
+
CopyTunerClient::HelperExtension.hook_render_to_string(
|
|
22
|
+
ActionController::Base,
|
|
23
|
+
middleware_enabled: CopyTunerClient.configuration.enable_middleware?
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
20
27
|
CopyTunerClient::TranslationLog.install_hook if CopyTunerClient.configuration.enable_middleware?
|
|
21
28
|
|
|
22
29
|
require 'copy_tuner_client/simple_form_extention'
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
require 'copy_tuner_client/copyray'
|
|
2
|
+
|
|
1
3
|
module CopyTunerClient
|
|
2
|
-
# ActionView の translate/t
|
|
4
|
+
# ActionView の translate/t を差し替えて Copyray のオーバーレイマーカーを注入し、注入してよい描画経路を制御する
|
|
3
5
|
module HelperExtension
|
|
4
6
|
# NOTE: class_eval ブロック内で def すると、ブロック内メソッドの複雑さが
|
|
5
7
|
# hook_translation_helper 自体の Metrics/AbcSize としてカウントされてしまうため、
|
|
@@ -50,14 +52,40 @@ module CopyTunerClient
|
|
|
50
52
|
# マーカー混入は実害が大きいため、controller の型で明示除外する。
|
|
51
53
|
return false if defined?(ActionMailer::Base) && current_controller.is_a?(ActionMailer::Base)
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
request = current_controller.request
|
|
56
|
+
return false unless passes_copyray_middleware?(request)
|
|
57
|
+
|
|
58
|
+
# NOTE: middleware を通っても render json: などは rewritable? に弾かれ書き換えられないため、
|
|
59
|
+
# format が html でない経路には注入しない。
|
|
60
|
+
request.format&.html? || false
|
|
56
61
|
end
|
|
57
62
|
private :copyray_injectable?
|
|
63
|
+
|
|
64
|
+
# NOTE: format が html でも出力が CopyrayMiddleware を通らない経路(ApplicationController.renderer や
|
|
65
|
+
# render_to_string)ではマーカーが除去されずに残るため、middleware が立てた印がある場合に限り注入する。
|
|
66
|
+
def passes_copyray_middleware?(request)
|
|
67
|
+
request&.env&.[](CopyTunerClient::Copyray::ENV_KEY)
|
|
68
|
+
end
|
|
69
|
+
private :passes_copyray_middleware?
|
|
58
70
|
end
|
|
59
71
|
private_constant :CopyrayCommentInjection
|
|
60
72
|
|
|
73
|
+
# NOTE: render_to_string の結果は PDF 化や JSON 埋め込みなどレスポンス以外に使われ、middleware の
|
|
74
|
+
# 書き換えを通らない。実行中だけ印を外し、その間の translate にマーカーを注入させない。
|
|
75
|
+
module RenderToStringGuard
|
|
76
|
+
def render_to_string(*, **, &)
|
|
77
|
+
env = request&.env
|
|
78
|
+
return super unless env&.delete(CopyTunerClient::Copyray::ENV_KEY)
|
|
79
|
+
|
|
80
|
+
begin
|
|
81
|
+
super
|
|
82
|
+
ensure
|
|
83
|
+
env[CopyTunerClient::Copyray::ENV_KEY] = true
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
private_constant :RenderToStringGuard
|
|
88
|
+
|
|
61
89
|
def self.hook_translation_helper(mod, middleware_enabled:)
|
|
62
90
|
return unless middleware_enabled
|
|
63
91
|
|
|
@@ -68,5 +96,11 @@ module CopyTunerClient
|
|
|
68
96
|
alias_method :t, :translate
|
|
69
97
|
end
|
|
70
98
|
end
|
|
99
|
+
|
|
100
|
+
def self.hook_render_to_string(mod, middleware_enabled:)
|
|
101
|
+
return unless middleware_enabled
|
|
102
|
+
|
|
103
|
+
mod.prepend(RenderToStringGuard)
|
|
104
|
+
end
|
|
71
105
|
end
|
|
72
106
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
require 'copy_tuner_client/copyray_middleware'
|
|
3
3
|
require 'copy_tuner_client/copyray/marker'
|
|
4
|
+
require 'copy_tuner_client/copyray'
|
|
4
5
|
require 'copy_tuner_client/translation_log'
|
|
5
6
|
|
|
6
7
|
describe CopyTunerClient::CopyrayMiddleware do
|
|
@@ -78,6 +79,19 @@ describe CopyTunerClient::CopyrayMiddleware do
|
|
|
78
79
|
end
|
|
79
80
|
end
|
|
80
81
|
|
|
82
|
+
context '下流の app を呼ぶとき' do
|
|
83
|
+
it '下流の app に渡る env にフラグが立っている' do
|
|
84
|
+
received_env = nil
|
|
85
|
+
recording_app =
|
|
86
|
+
->(env) do
|
|
87
|
+
received_env = env.dup
|
|
88
|
+
[200, { 'Content-Type' => 'application/json' }, ['{}']]
|
|
89
|
+
end
|
|
90
|
+
described_class.new(recording_app).call({})
|
|
91
|
+
expect(received_env[CopyTunerClient::Copyray::ENV_KEY]).to be true
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
81
95
|
context 'HTML 以外のレスポンスのとき' do
|
|
82
96
|
let(:headers) { { 'Content-Type' => 'application/json' } }
|
|
83
97
|
let(:body) { "{\"x\":\"#{marker('a.b')}\"}" }
|
|
@@ -17,7 +17,12 @@ describe CopyTunerClient::HelperExtension do
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def request_class
|
|
20
|
-
@request_class ||= Struct.new(:format)
|
|
20
|
+
@request_class ||= Struct.new(:format, :env)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# NOTE: CopyrayMiddleware を通るリクエストを再現するため、フラグ付きの env を持つ request を作る。
|
|
24
|
+
def injectable_request(format = :html)
|
|
25
|
+
request_class.new(format_class.new(format), { CopyTunerClient::Copyray::ENV_KEY => true })
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
def controller_class
|
|
@@ -44,7 +49,7 @@ describe CopyTunerClient::HelperExtension do
|
|
|
44
49
|
# デフォルトはそれを再現した controller を持たせておく。
|
|
45
50
|
let(:view) do
|
|
46
51
|
Class.new.include(keyword_arguments_helper).new.tap do |v|
|
|
47
|
-
v.controller = controller_class.new(
|
|
52
|
+
v.controller = controller_class.new(injectable_request)
|
|
48
53
|
end
|
|
49
54
|
end
|
|
50
55
|
|
|
@@ -84,10 +89,20 @@ describe CopyTunerClient::HelperExtension do
|
|
|
84
89
|
|
|
85
90
|
%i[json text csv pdf].each do |format|
|
|
86
91
|
it "does not inject the marker when request.format is :#{format}" do
|
|
87
|
-
view.controller = controller_class.new(
|
|
92
|
+
view.controller = controller_class.new(injectable_request(format))
|
|
88
93
|
expect(view.translate('some.key', name: 'World')).to eq 'Hello, World'
|
|
89
94
|
end
|
|
90
95
|
end
|
|
96
|
+
|
|
97
|
+
it 'format が html でも env にフラグが無ければ注入しない' do
|
|
98
|
+
view.controller = controller_class.new(request_class.new(format_class.new(:html), {}))
|
|
99
|
+
expect(view.translate('some.key', name: 'World')).to eq 'Hello, World'
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'request.env が nil でも落ちずに注入しない' do
|
|
103
|
+
view.controller = controller_class.new(request_class.new(format_class.new(:html), nil))
|
|
104
|
+
expect(view.translate('some.key', name: 'World')).to eq 'Hello, World'
|
|
105
|
+
end
|
|
91
106
|
end
|
|
92
107
|
|
|
93
108
|
context 'injection guard by controller' do
|
|
@@ -109,7 +124,7 @@ describe CopyTunerClient::HelperExtension do
|
|
|
109
124
|
|
|
110
125
|
it 'does not raise when ActionMailer is not loaded' do
|
|
111
126
|
hide_const('ActionMailer::Base') if defined?(ActionMailer::Base)
|
|
112
|
-
view.controller = controller_class.new(
|
|
127
|
+
view.controller = controller_class.new(injectable_request)
|
|
113
128
|
expect { view.translate('some.key', name: 'World') }.not_to raise_error
|
|
114
129
|
end
|
|
115
130
|
end
|
|
@@ -118,10 +133,91 @@ describe CopyTunerClient::HelperExtension do
|
|
|
118
133
|
# 維持されなければならない。注入ガードが初期値登録まで巻き添えで止めていないことを保証する。
|
|
119
134
|
context 'default value registration' do
|
|
120
135
|
it 'registers the default value even when the marker is not injected' do
|
|
121
|
-
view.controller = controller_class.new(
|
|
136
|
+
view.controller = controller_class.new(injectable_request(:json))
|
|
122
137
|
allow(I18n).to receive(:t)
|
|
123
138
|
view.translate('some.key', name: 'World', default: 'Default')
|
|
124
139
|
expect(I18n).to have_received(:t).with('some.key', hash_including(default: 'Default'))
|
|
125
140
|
end
|
|
126
141
|
end
|
|
142
|
+
|
|
143
|
+
describe '.hook_render_to_string' do
|
|
144
|
+
# NOTE: ActionController::Base を読み込まずに検証するため、super 呼び出し時の env の状態を
|
|
145
|
+
# 記録するだけの最小の基底クラスに prepend する。
|
|
146
|
+
let(:base_class) do
|
|
147
|
+
Class.new do
|
|
148
|
+
attr_reader :request, :recorded
|
|
149
|
+
|
|
150
|
+
def initialize(request)
|
|
151
|
+
@request = request
|
|
152
|
+
@recorded = []
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def render_to_string(*_args, raise_error: false, nested: false)
|
|
156
|
+
record_flag
|
|
157
|
+
if nested
|
|
158
|
+
render_to_string
|
|
159
|
+
record_flag
|
|
160
|
+
end
|
|
161
|
+
raise 'boom' if raise_error
|
|
162
|
+
|
|
163
|
+
'rendered'
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def record_flag
|
|
167
|
+
@recorded << request&.env&.key?(CopyTunerClient::Copyray::ENV_KEY)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
let(:env) { { CopyTunerClient::Copyray::ENV_KEY => true } }
|
|
172
|
+
let(:controller) { base_class.new(request_class.new(format_class.new(:html), env)) }
|
|
173
|
+
|
|
174
|
+
context 'middleware_enabled が true のとき' do
|
|
175
|
+
before { described_class.hook_render_to_string(base_class, middleware_enabled: true) }
|
|
176
|
+
|
|
177
|
+
it '実行中は env からフラグが外れる' do
|
|
178
|
+
controller.render_to_string
|
|
179
|
+
expect(controller.recorded).to eq [false]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it '元の戻り値を返す' do
|
|
183
|
+
expect(controller.render_to_string).to eq 'rendered'
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it '終了後はフラグが復元される' do
|
|
187
|
+
controller.render_to_string
|
|
188
|
+
expect(env[CopyTunerClient::Copyray::ENV_KEY]).to be true
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it '例外が起きてもフラグが復元される' do
|
|
192
|
+
expect { controller.render_to_string(raise_error: true) }.to raise_error('boom')
|
|
193
|
+
expect(env[CopyTunerClient::Copyray::ENV_KEY]).to be true
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it '入れ子でも外側の終了時まで外れたまま' do
|
|
197
|
+
controller.render_to_string(nested: true)
|
|
198
|
+
expect(controller.recorded).to eq [false, false, false]
|
|
199
|
+
expect(env[CopyTunerClient::Copyray::ENV_KEY]).to be true
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it '元々フラグが無い env にはフラグを足さない' do
|
|
203
|
+
env.clear
|
|
204
|
+
controller.render_to_string
|
|
205
|
+
expect(env).not_to have_key(CopyTunerClient::Copyray::ENV_KEY)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it 'request が nil でも落ちない' do
|
|
209
|
+
controller = base_class.new(nil)
|
|
210
|
+
expect(controller.render_to_string).to eq 'rendered'
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
context 'middleware_enabled が false のとき' do
|
|
215
|
+
before { described_class.hook_render_to_string(base_class, middleware_enabled: false) }
|
|
216
|
+
|
|
217
|
+
it 'フックせず実行中もフラグが残る' do
|
|
218
|
+
controller.render_to_string
|
|
219
|
+
expect(controller.recorded).to eq [true]
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
127
223
|
end
|