copy_tuner_client 2.1.2 → 2.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/.gitignore +5 -0
- data/.rubocop.yml +46 -1
- data/CLAUDE.md +1 -0
- data/Gemfile +1 -0
- data/copy_tuner_client.gemspec +3 -3
- data/gemfiles/8.0.gemfile +3 -3
- data/gemfiles/8.1.gemfile +3 -3
- data/gemfiles/main.gemfile +3 -3
- data/lib/copy_tuner_client/cache.rb +34 -27
- data/lib/copy_tuner_client/client.rb +35 -23
- data/lib/copy_tuner_client/configuration.rb +62 -40
- data/lib/copy_tuner_client/copyray/rewriter.rb +1 -1
- data/lib/copy_tuner_client/copyray.rb +1 -0
- data/lib/copy_tuner_client/copyray_middleware.rb +31 -24
- data/lib/copy_tuner_client/dotted_hash.rb +7 -7
- data/lib/copy_tuner_client/engine.rb +4 -6
- data/lib/copy_tuner_client/helper_extension.rb +58 -54
- data/lib/copy_tuner_client/i18n_backend.rb +12 -14
- data/lib/copy_tuner_client/i18n_compat.rb +2 -1
- data/lib/copy_tuner_client/poller.rb +3 -4
- data/lib/copy_tuner_client/prefixed_logger.rb +13 -12
- data/lib/copy_tuner_client/process_guard.rb +29 -26
- data/lib/copy_tuner_client/queue_with_timeout.rb +29 -19
- data/lib/copy_tuner_client/rails.rb +9 -8
- data/lib/copy_tuner_client/request_sync.rb +26 -24
- data/lib/copy_tuner_client/simple_form_extention.rb +10 -6
- data/lib/copy_tuner_client/translation_log.rb +10 -5
- data/lib/copy_tuner_client/version.rb +1 -1
- data/lib/copy_tuner_client.rb +4 -8
- data/lib/tasks/copy_tuner_client_tasks.rake +14 -16
- data/skills/copy-tuner-to-locales-migrate-prefix/scripts/migrate_prefix.rb +14 -7
- data/skills/copy-tuner-to-t-migrate/scripts/migrate_tt.rb +19 -17
- data/spec/copy_tuner_client/cache_spec.rb +63 -57
- data/spec/copy_tuner_client/client_spec.rb +82 -81
- data/spec/copy_tuner_client/configuration_spec.rb +223 -222
- data/spec/copy_tuner_client/copyray/marker_spec.rb +2 -1
- data/spec/copy_tuner_client/copyray/rewriter_spec.rb +15 -10
- data/spec/copy_tuner_client/copyray_middleware_spec.rb +9 -6
- data/spec/copy_tuner_client/copyray_spec.rb +8 -8
- data/spec/copy_tuner_client/dotted_hash_spec.rb +33 -33
- data/spec/copy_tuner_client/helper_extension_spec.rb +47 -44
- data/spec/copy_tuner_client/i18n_backend_spec.rb +144 -143
- data/spec/copy_tuner_client/poller_spec.rb +25 -24
- data/spec/copy_tuner_client/prefixed_logger_spec.rb +15 -12
- data/spec/copy_tuner_client/process_guard_spec.rb +27 -27
- data/spec/copy_tuner_client/request_sync_spec.rb +51 -58
- data/spec/copy_tuner_client/translation_log_spec.rb +38 -24
- data/spec/copy_tuner_client_spec.rb +5 -6
- data/spec/support/client_spec_helpers.rb +1 -1
- data/spec/support/defines_constants.rb +3 -34
- data/spec/support/fake_client.rb +1 -3
- data/spec/support/fake_copy_tuner_app.rb +54 -62
- data/spec/support/fake_html_safe_string.rb +2 -3
- data/spec/support/fake_logger.rb +22 -21
- data/spec/support/fake_passenger.rb +4 -6
- data/spec/support/fake_unicorn.rb +2 -4
- data/spec/support/middleware_stack.rb +3 -3
- data/spec/support/writing_cache.rb +4 -4
- metadata +1 -1
|
@@ -1,33 +1,32 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe CopyTunerClient::Poller do
|
|
4
|
-
POLLING_DELAY = 0.5
|
|
5
|
-
|
|
6
4
|
let(:client) { FakeClient.new }
|
|
7
|
-
let(:cache) { CopyTunerClient::Cache.new(client, :
|
|
5
|
+
let(:cache) { CopyTunerClient::Cache.new(client, logger: FakeLogger.new) }
|
|
6
|
+
let!(:pollers) { [] }
|
|
7
|
+
|
|
8
|
+
def polling_delay
|
|
9
|
+
0.5
|
|
10
|
+
end
|
|
8
11
|
|
|
9
12
|
def build_poller(config = {})
|
|
10
13
|
config[:logger] ||= FakeLogger.new
|
|
11
|
-
config[:polling_delay] =
|
|
14
|
+
config[:polling_delay] = polling_delay
|
|
12
15
|
default_config = CopyTunerClient::Configuration.new.to_hash
|
|
13
16
|
poller = CopyTunerClient::Poller.new(cache, default_config.update(config))
|
|
14
|
-
|
|
17
|
+
pollers << poller
|
|
15
18
|
poller
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def wait_for_next_sync
|
|
19
|
-
sleep(
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
before do
|
|
23
|
-
@pollers = []
|
|
22
|
+
sleep(polling_delay * 3)
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
after do
|
|
27
|
-
|
|
26
|
+
pollers.each(&:stop)
|
|
28
27
|
end
|
|
29
28
|
|
|
30
|
-
it
|
|
29
|
+
it 'polls after being started' do
|
|
31
30
|
poller = build_poller
|
|
32
31
|
poller.start
|
|
33
32
|
|
|
@@ -37,8 +36,8 @@ describe CopyTunerClient::Poller do
|
|
|
37
36
|
expect(cache['test.key']).to eq('value')
|
|
38
37
|
end
|
|
39
38
|
|
|
40
|
-
it "
|
|
41
|
-
|
|
39
|
+
it "doesn't poll before being started" do
|
|
40
|
+
build_poller
|
|
42
41
|
client['test.key'] = 'value'
|
|
43
42
|
|
|
44
43
|
wait_for_next_sync
|
|
@@ -46,7 +45,7 @@ describe CopyTunerClient::Poller do
|
|
|
46
45
|
expect(cache['test.key']).to be_nil
|
|
47
46
|
end
|
|
48
47
|
|
|
49
|
-
it
|
|
48
|
+
it 'stops polling when stopped' do
|
|
50
49
|
poller = build_poller
|
|
51
50
|
|
|
52
51
|
poller.start
|
|
@@ -58,12 +57,12 @@ describe CopyTunerClient::Poller do
|
|
|
58
57
|
expect(cache['test.key']).to be_nil
|
|
59
58
|
end
|
|
60
59
|
|
|
61
|
-
it
|
|
62
|
-
failure =
|
|
60
|
+
it 'stops polling with an invalid api key' do
|
|
61
|
+
failure = 'server is napping'
|
|
63
62
|
logger = FakeLogger.new
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
poller = build_poller(:
|
|
64
|
+
allow(cache).to receive(:download).and_raise(CopyTunerClient::InvalidApiKey.new(failure))
|
|
65
|
+
poller = build_poller(logger:)
|
|
67
66
|
|
|
68
67
|
cache['upload.key'] = 'upload'
|
|
69
68
|
poller.start
|
|
@@ -78,20 +77,22 @@ describe CopyTunerClient::Poller do
|
|
|
78
77
|
end
|
|
79
78
|
|
|
80
79
|
it "logs an error if the background thread can't start" do
|
|
81
|
-
|
|
80
|
+
allow(Thread).to receive(:new).and_return(nil)
|
|
82
81
|
logger = FakeLogger.new
|
|
83
82
|
|
|
84
|
-
build_poller(:
|
|
83
|
+
build_poller(logger:).start
|
|
85
84
|
|
|
86
85
|
expect(logger).to have_entry(:error, "Couldn't start poller thread")
|
|
87
86
|
end
|
|
88
87
|
|
|
89
|
-
it
|
|
88
|
+
it 'flushes the log when polling' do
|
|
90
89
|
logger = FakeLogger.new
|
|
91
|
-
|
|
90
|
+
allow(logger).to receive(:flush)
|
|
92
91
|
|
|
93
|
-
build_poller(:
|
|
92
|
+
build_poller(logger:).start
|
|
94
93
|
|
|
95
94
|
wait_for_next_sync
|
|
95
|
+
|
|
96
|
+
expect(logger).to have_received(:flush).at_least(:once)
|
|
96
97
|
end
|
|
97
98
|
end
|
|
@@ -1,35 +1,38 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe CopyTunerClient::PrefixedLogger do
|
|
4
|
+
subject(:prefixed_logger) { described_class.new(prefix, output_logger) }
|
|
5
|
+
|
|
4
6
|
let(:output_logger) { FakeLogger.new }
|
|
5
|
-
let(:prefix) {
|
|
7
|
+
let(:prefix) { '** NOTICE:' }
|
|
6
8
|
let(:thread_info) { "[P:#{Process.pid}] [T:#{Thread.current.object_id}]" }
|
|
7
|
-
subject { CopyTunerClient::PrefixedLogger.new(prefix, output_logger) }
|
|
8
9
|
|
|
9
|
-
it
|
|
10
|
-
expect(
|
|
10
|
+
it 'provides the prefix' do
|
|
11
|
+
expect(prefixed_logger.prefix).to eq(prefix)
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
it
|
|
14
|
-
expect(
|
|
14
|
+
it 'provides the logger' do
|
|
15
|
+
expect(prefixed_logger.original_logger).to eq(output_logger)
|
|
15
16
|
end
|
|
16
17
|
|
|
17
|
-
[
|
|
18
|
+
%i[debug info warn error fatal].each do |level|
|
|
18
19
|
it "prefixes #{level} log messages" do
|
|
19
20
|
message = 'hello'
|
|
20
|
-
|
|
21
|
+
prefixed_logger.public_send(level, message)
|
|
21
22
|
|
|
22
23
|
expect(output_logger).to have_entry(level, "#{prefix} #{thread_info} #{message}")
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
it
|
|
27
|
-
|
|
27
|
+
it 'calls flush for a logger that responds to flush' do
|
|
28
|
+
allow(output_logger).to receive(:flush)
|
|
29
|
+
|
|
30
|
+
prefixed_logger.flush
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
expect(output_logger).to have_received(:flush)
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
it "doesn't call flush for a logger that doesn't respond to flush" do
|
|
33
|
-
expect {
|
|
36
|
+
expect { prefixed_logger.flush }.not_to raise_error
|
|
34
37
|
end
|
|
35
38
|
end
|
|
@@ -3,17 +3,14 @@ require 'spec_helper'
|
|
|
3
3
|
describe CopyTunerClient::ProcessGuard do
|
|
4
4
|
include DefinesConstants
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
let!(:original_process_name) { $PROGRAM_NAME }
|
|
7
|
+
let(:cache) { instance_double(CopyTunerClient::Cache, flush: nil) }
|
|
8
|
+
let(:poller) { instance_double(CopyTunerClient::Poller, start: nil) }
|
|
9
9
|
|
|
10
10
|
after do
|
|
11
|
-
$0 =
|
|
11
|
+
$0 = original_process_name
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
let(:cache) { double('cache', flush: nil) }
|
|
15
|
-
let(:poller) { double('poller', start: nil) }
|
|
16
|
-
|
|
17
14
|
def build_process_guard(options = {})
|
|
18
15
|
preserve_exit_hook = options.delete(:preserve_exit_hook)
|
|
19
16
|
options[:logger] ||= FakeLogger.new
|
|
@@ -23,67 +20,70 @@ describe CopyTunerClient::ProcessGuard do
|
|
|
23
20
|
process_guard
|
|
24
21
|
end
|
|
25
22
|
|
|
26
|
-
it
|
|
27
|
-
expect(poller).to receive(:start)
|
|
23
|
+
it 'starts polling from a worker process' do
|
|
28
24
|
process_guard = build_process_guard
|
|
29
25
|
process_guard.start
|
|
26
|
+
|
|
27
|
+
expect(poller).to have_received(:start)
|
|
30
28
|
end
|
|
31
29
|
|
|
32
|
-
it
|
|
33
|
-
expect(poller).not_to receive(:start)
|
|
30
|
+
it 'registers passenger hooks from the passenger master' do
|
|
34
31
|
logger = FakeLogger.new
|
|
35
32
|
passenger = define_constant('PhusionPassenger', FakePassenger.new)
|
|
36
33
|
passenger.become_master
|
|
37
34
|
|
|
38
|
-
process_guard = build_process_guard(:
|
|
35
|
+
process_guard = build_process_guard(logger:)
|
|
39
36
|
process_guard.start
|
|
40
37
|
|
|
41
|
-
expect(
|
|
38
|
+
expect(poller).not_to have_received(:start)
|
|
39
|
+
expect(logger).to have_entry(:info, 'Registered Phusion Passenger fork hook')
|
|
42
40
|
end
|
|
43
41
|
|
|
44
|
-
it
|
|
45
|
-
expect(poller).to receive(:start)
|
|
42
|
+
it 'starts polling from a passenger worker' do
|
|
46
43
|
logger = FakeLogger.new
|
|
47
44
|
passenger = define_constant('PhusionPassenger', FakePassenger.new)
|
|
48
45
|
passenger.become_master
|
|
49
|
-
process_guard = build_process_guard(:
|
|
46
|
+
process_guard = build_process_guard(logger:)
|
|
50
47
|
|
|
51
48
|
process_guard.start
|
|
52
49
|
passenger.spawn
|
|
50
|
+
|
|
51
|
+
expect(poller).to have_received(:start)
|
|
53
52
|
end
|
|
54
53
|
|
|
55
|
-
it
|
|
56
|
-
expect(poller).not_to receive(:start)
|
|
54
|
+
it 'registers unicorn hooks from the unicorn master' do
|
|
57
55
|
logger = FakeLogger.new
|
|
58
56
|
define_constant('Unicorn', Module.new)
|
|
59
57
|
http_server = Class.new(FakeUnicornServer)
|
|
60
58
|
unicorn = define_constant('Unicorn::HttpServer', http_server).new
|
|
61
59
|
unicorn.become_master
|
|
62
60
|
|
|
63
|
-
process_guard = build_process_guard(:
|
|
61
|
+
process_guard = build_process_guard(logger:)
|
|
64
62
|
process_guard.start
|
|
65
63
|
|
|
66
|
-
expect(
|
|
64
|
+
expect(poller).not_to have_received(:start)
|
|
65
|
+
expect(logger).to have_entry(:info, 'Registered Unicorn fork hook')
|
|
67
66
|
end
|
|
68
67
|
|
|
69
|
-
it
|
|
70
|
-
expect(poller).to receive(:start)
|
|
68
|
+
it 'starts polling from a unicorn worker' do
|
|
71
69
|
logger = FakeLogger.new
|
|
72
70
|
define_constant('Unicorn', Module.new)
|
|
73
71
|
http_server = Class.new(FakeUnicornServer)
|
|
74
72
|
unicorn = define_constant('Unicorn::HttpServer', http_server).new
|
|
75
73
|
unicorn.become_master
|
|
76
|
-
process_guard = build_process_guard(:
|
|
74
|
+
process_guard = build_process_guard(logger:)
|
|
77
75
|
|
|
78
76
|
process_guard.start
|
|
79
77
|
unicorn.spawn
|
|
78
|
+
|
|
79
|
+
expect(poller).to have_received(:start)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
xit "flushes when the process terminates" do
|
|
82
|
+
it 'flushes when the process terminates' do
|
|
84
83
|
cache = WritingCache.new
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
FileUtils.rm_f(File.join(PROJECT_ROOT, 'tmp', 'written_cache'))
|
|
85
|
+
fork do
|
|
86
|
+
process_guard = build_process_guard(cache:, preserve_exit_hook: true)
|
|
87
87
|
process_guard.start
|
|
88
88
|
exit
|
|
89
89
|
end
|
|
@@ -4,80 +4,73 @@ describe CopyTunerClient::RequestSync do
|
|
|
4
4
|
let(:poller) { {} }
|
|
5
5
|
let(:cache) { {} }
|
|
6
6
|
let(:response) { 'response' }
|
|
7
|
-
let(:
|
|
8
|
-
|
|
7
|
+
let(:app) { double('app', call: response) }
|
|
8
|
+
|
|
9
9
|
before do
|
|
10
|
-
allow(cache).to receive_messages(:
|
|
10
|
+
allow(cache).to receive_messages(flush: nil, download: nil)
|
|
11
11
|
allow(poller).to receive(:start_sync).and_return(nil)
|
|
12
12
|
end
|
|
13
|
-
subject { CopyTunerClient::RequestSync.new(app, :poller => poller, :cache => cache, :interval => 0) }
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
result = subject.call(env)
|
|
18
|
-
expect(result).to eq(response)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
14
|
+
context 'interval が 0 の場合' do
|
|
15
|
+
subject(:request_sync) { described_class.new(app, poller:, cache:, interval: 0) }
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
let(:env) do
|
|
24
|
-
{ "PATH_INFO" => '/assets/choper.png' }
|
|
25
|
-
end
|
|
26
|
-
let(:poller) { {} }
|
|
27
|
-
let(:cache) { {} }
|
|
28
|
-
let(:response) { 'response' }
|
|
29
|
-
let(:app) { double('app', :call => response) }
|
|
30
|
-
before do
|
|
31
|
-
allow(cache).to receive_messages(:flush => nil, :download => nil)
|
|
32
|
-
allow(poller).to receive(:start_sync).and_return(nil)
|
|
33
|
-
end
|
|
34
|
-
subject { CopyTunerClient::RequestSync.new(app, :poller => poller, :cache => cache, :interval => 0) }
|
|
17
|
+
let(:env) { 'env' }
|
|
35
18
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
19
|
+
it 'invokes the upstream app' do
|
|
20
|
+
result = request_sync.call(env)
|
|
21
|
+
expect(app).to have_received(:call).with(env)
|
|
22
|
+
expect(result).to eq(response)
|
|
23
|
+
end
|
|
41
24
|
end
|
|
42
|
-
end
|
|
43
25
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
let(:cache) { {} }
|
|
47
|
-
let(:response) { 'response' }
|
|
48
|
-
let(:env) { 'env' }
|
|
49
|
-
let(:app) { double('app', :call => response) }
|
|
50
|
-
subject { CopyTunerClient::RequestSync.new(app, :poller => poller, :cache => cache, :interval => 10) }
|
|
51
|
-
before do
|
|
52
|
-
allow(cache).to receive_messages(:flush => nil, :download => nil)
|
|
53
|
-
allow(poller).to receive(:start_sync).and_return(nil)
|
|
54
|
-
end
|
|
26
|
+
context 'when serving assets' do
|
|
27
|
+
subject(:request_sync) { described_class.new(app, poller:, cache:, interval: 0) }
|
|
55
28
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
expect(cache).to receive(:download).once
|
|
59
|
-
subject.call(env)
|
|
29
|
+
let(:env) do
|
|
30
|
+
{ 'PATH_INFO' => '/assets/choper.png' }
|
|
60
31
|
end
|
|
61
|
-
end
|
|
62
32
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
subject.call(env)
|
|
33
|
+
it "don't start sync" do
|
|
34
|
+
request_sync.call(env)
|
|
35
|
+
request_sync.call(env)
|
|
67
36
|
|
|
68
|
-
expect(
|
|
69
|
-
|
|
37
|
+
expect(cache).to have_received(:download).once
|
|
38
|
+
expect(poller).not_to have_received(:start_sync)
|
|
70
39
|
end
|
|
71
40
|
end
|
|
72
41
|
|
|
73
|
-
context '
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
42
|
+
context 'interval が 10 の場合' do
|
|
43
|
+
subject(:request_sync) { described_class.new(app, poller:, cache:, interval: 10) }
|
|
44
|
+
|
|
45
|
+
let(:env) { 'env' }
|
|
46
|
+
|
|
47
|
+
context 'first request' do
|
|
48
|
+
it 'download' do
|
|
49
|
+
request_sync.call(env)
|
|
50
|
+
expect(cache).to have_received(:download).once
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'in interval request' do
|
|
55
|
+
it 'does not start sync for the second time' do
|
|
56
|
+
request_sync.call(env)
|
|
57
|
+
request_sync.call(env)
|
|
58
|
+
|
|
59
|
+
expect(cache).to have_received(:download).once
|
|
60
|
+
expect(poller).not_to have_received(:start_sync)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'over interval request' do
|
|
65
|
+
it 'start sync for the second time' do
|
|
66
|
+
request_sync.call(env)
|
|
67
|
+
|
|
68
|
+
request_sync.last_synced = Time.now - 60
|
|
69
|
+
request_sync.call(env)
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
expect(cache).to have_received(:download).once
|
|
72
|
+
expect(poller).to have_received(:start_sync).once
|
|
73
|
+
end
|
|
81
74
|
end
|
|
82
75
|
end
|
|
83
76
|
end
|
|
@@ -5,46 +5,60 @@ describe CopyTunerClient::TranslationLog do
|
|
|
5
5
|
before { described_class.clear }
|
|
6
6
|
|
|
7
7
|
describe '.add' do
|
|
8
|
-
context '
|
|
9
|
-
|
|
8
|
+
context '初期化済みかつキーが local_first_key_regexp にマッチする場合' do
|
|
9
|
+
before { CopyTunerClient.configuration.local_first_key_regexp = /\Aviews\./ }
|
|
10
|
+
|
|
11
|
+
it 'マッチしたキーを記録しないこと' do
|
|
12
|
+
described_class.add('views.foo', 'Hello')
|
|
13
|
+
expect(described_class.translations).to be_empty
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'マッチしないキーは記録すること' do
|
|
17
|
+
described_class.add('messages.greeting', 'Hi')
|
|
18
|
+
expect(described_class.translations).to eq('messages.greeting' => 'Hi')
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context '初期化済みかつ local_first_key_regexp が未設定の場合' do
|
|
23
|
+
it 'キーを記録すること' do
|
|
10
24
|
described_class.add('views.foo', 'Hello')
|
|
11
25
|
expect(described_class.translations).to eq('views.foo' => 'Hello')
|
|
12
26
|
end
|
|
13
27
|
|
|
14
|
-
it '
|
|
28
|
+
it '既存のキーを上書きしないこと' do
|
|
15
29
|
described_class.add('views.foo', 'Hello')
|
|
16
30
|
described_class.add('views.foo', 'World')
|
|
17
31
|
expect(described_class.translations['views.foo']).to eq 'Hello'
|
|
18
32
|
end
|
|
33
|
+
end
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
it 'does not record the matching key' do
|
|
24
|
-
described_class.add('views.foo', 'Hello')
|
|
25
|
-
expect(described_class.translations).to be_empty
|
|
26
|
-
end
|
|
35
|
+
context '未初期化の場合' do
|
|
36
|
+
before { Thread.current[:translations] = nil }
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
end
|
|
38
|
+
it 'キーを無視すること' do
|
|
39
|
+
described_class.add('views.foo', 'Hello')
|
|
40
|
+
expect(described_class.initialized?).to be false
|
|
32
41
|
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
33
44
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
describe '.install_hook' do
|
|
46
|
+
# フック導入前後で I18n の特異クラスを復元し、他のテストへの副作用を防ぐ
|
|
47
|
+
around do |example|
|
|
48
|
+
original_singleton_methods = I18n.singleton_class.instance_methods(false)
|
|
49
|
+
example.run
|
|
50
|
+
(I18n.singleton_class.instance_methods(false) - original_singleton_methods).each do |method_name|
|
|
51
|
+
I18n.singleton_class.__send__(:remove_method, method_name)
|
|
39
52
|
end
|
|
40
53
|
end
|
|
41
54
|
|
|
42
|
-
context '
|
|
43
|
-
before {
|
|
55
|
+
context 'ミドルウェアが有効な場合' do
|
|
56
|
+
before { allow(CopyTunerClient.configuration).to receive(:enable_middleware?).and_return(true) }
|
|
44
57
|
|
|
45
|
-
it '
|
|
46
|
-
described_class.
|
|
47
|
-
expect(
|
|
58
|
+
it 'エラーを発生させずに I18n.translate をフックすること' do
|
|
59
|
+
expect { described_class.install_hook }.not_to raise_error
|
|
60
|
+
expect(I18n.translate(:hello, default: 'Hello')).to eq 'Hello'
|
|
61
|
+
expect(described_class.translations).to have_key('hello')
|
|
48
62
|
end
|
|
49
63
|
end
|
|
50
64
|
end
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe CopyTunerClient do
|
|
4
|
-
|
|
5
4
|
before do
|
|
6
|
-
allow(
|
|
5
|
+
allow(described_class.configuration).to receive_messages(cache: 'cache', client: 'client')
|
|
7
6
|
end
|
|
8
7
|
|
|
9
8
|
it 'delegates cache to the configuration object' do
|
|
10
|
-
expect(
|
|
11
|
-
expect(
|
|
9
|
+
expect(described_class.cache).to eq('cache')
|
|
10
|
+
expect(described_class.configuration).to have_received(:cache).once
|
|
12
11
|
end
|
|
13
12
|
|
|
14
13
|
it 'delegates client to the configuration object' do
|
|
15
|
-
expect(
|
|
16
|
-
expect(
|
|
14
|
+
expect(described_class.client).to eq('client')
|
|
15
|
+
expect(described_class.configuration).to have_received(:client).once
|
|
17
16
|
end
|
|
18
17
|
end
|
|
@@ -1,44 +1,13 @@
|
|
|
1
1
|
module DefinesConstants
|
|
2
|
-
def
|
|
3
|
-
super
|
|
4
|
-
example_group.class_eval do
|
|
5
|
-
before { @defined_constants = [] }
|
|
6
|
-
after { undefine_constants }
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def define_class(class_name, base = Object, &block)
|
|
2
|
+
def define_class(class_name, base = Object, &)
|
|
11
3
|
class_name = class_name.to_s.camelize
|
|
12
4
|
klass = Class.new(base)
|
|
13
5
|
define_constant(class_name, klass)
|
|
14
|
-
klass.class_eval(&
|
|
6
|
+
klass.class_eval(&) if block_given?
|
|
15
7
|
klass
|
|
16
8
|
end
|
|
17
9
|
|
|
18
10
|
def define_constant(path, value)
|
|
19
|
-
|
|
20
|
-
parent.const_set(name, value)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
@defined_constants << path
|
|
24
|
-
value
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def parse_constant(path)
|
|
28
|
-
parent_names = path.split('::')
|
|
29
|
-
name = parent_names.pop
|
|
30
|
-
parent = parent_names.inject(Object) do |ref, child_name|
|
|
31
|
-
ref.const_get(child_name)
|
|
32
|
-
end
|
|
33
|
-
yield(parent, name)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def undefine_constants
|
|
37
|
-
@defined_constants.reverse.each do |path|
|
|
38
|
-
parse_constant(path) do |parent, name|
|
|
39
|
-
parent.send(:remove_const, name)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
11
|
+
stub_const(path, value)
|
|
42
12
|
end
|
|
43
13
|
end
|
|
44
|
-
|