copy_tuner_client 0.4.5 → 0.4.6
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 +5 -5
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +1 -1
- data/lib/copy_tuner_client.rb +4 -9
- data/lib/copy_tuner_client/cache.rb +5 -4
- data/lib/copy_tuner_client/client.rb +5 -3
- data/lib/copy_tuner_client/configuration.rb +19 -5
- data/lib/copy_tuner_client/poller.rb +31 -10
- data/lib/copy_tuner_client/queue_with_timeout.rb +50 -0
- data/lib/copy_tuner_client/request_sync.rb +15 -2
- data/lib/copy_tuner_client/version.rb +1 -1
- data/spec/copy_tuner_client/poller_spec.rb +0 -10
- data/spec/copy_tuner_client/request_sync_spec.rb +33 -43
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bea0580a939cf024f1fdf8591aaa72956c1e3aeabdea1609a8f97d3ebb544890
|
4
|
+
data.tar.gz: 9beccd0ea02ef6ee6b5283ce60ac442b1d6bcd3c5876907bcacb899ce5fbabf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5efb8eb5d2ca1db876223bf4ab29307caf2104f83cff25d4583f74e409cbb30252b7cc9b6b6f28a4c2ca0d8e73671e6014ec04a8dd137b98b20b2cfd3b4099e2
|
7
|
+
data.tar.gz: 17b5e90d9091dc0b95bbba213d5645a25b250f9c44e8ef6dd302447ec7de2e0d49b07c3fb022d0748ae8fdbf857716573daf8488db2f3ce033a110ef376c34bd
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/lib/copy_tuner_client.rb
CHANGED
@@ -12,10 +12,6 @@ module CopyTunerClient
|
|
12
12
|
# Must act like a hash and return sensible values for all CopyTuner
|
13
13
|
# configuration options. Usually set when {.configure} is called.
|
14
14
|
attr_accessor :configuration
|
15
|
-
|
16
|
-
# @return [Poller] instance used to poll for changes.
|
17
|
-
# This is set when {.configure} is called.
|
18
|
-
attr_accessor :poller
|
19
15
|
end
|
20
16
|
|
21
17
|
# Issues a new deploy, marking all draft blurbs as published.
|
@@ -30,11 +26,6 @@ module CopyTunerClient
|
|
30
26
|
cache.export
|
31
27
|
end
|
32
28
|
|
33
|
-
# Starts the polling process.
|
34
|
-
def self.start_poller
|
35
|
-
poller.start
|
36
|
-
end
|
37
|
-
|
38
29
|
# Flush queued changed synchronously
|
39
30
|
def self.flush
|
40
31
|
cache.flush
|
@@ -48,6 +39,10 @@ module CopyTunerClient
|
|
48
39
|
CopyTunerClient.configuration.client
|
49
40
|
end
|
50
41
|
|
42
|
+
def self.poller
|
43
|
+
CopyTunerClient.configuration.poller
|
44
|
+
end
|
45
|
+
|
51
46
|
# Call this method to modify defaults in your initializers.
|
52
47
|
#
|
53
48
|
# @example
|
@@ -13,15 +13,16 @@ module CopyTunerClient
|
|
13
13
|
# @param options [Hash]
|
14
14
|
# @option options [Logger] :logger where errors should be logged
|
15
15
|
def initialize(client, options)
|
16
|
-
@blurbs = {}
|
17
16
|
@client = client
|
18
|
-
@downloaded = false
|
19
17
|
@logger = options[:logger]
|
20
18
|
@mutex = Mutex.new
|
21
|
-
@queued = {}
|
22
|
-
@started = false
|
23
19
|
@exclude_key_regexp = options[:exclude_key_regexp]
|
24
20
|
@locales = Array(options[:locales]).map(&:to_s)
|
21
|
+
# mutable states
|
22
|
+
@blurbs = {}
|
23
|
+
@queued = {}
|
24
|
+
@started = false
|
25
|
+
@downloaded = false
|
25
26
|
end
|
26
27
|
|
27
28
|
# Returns content for the given blurb.
|
@@ -47,13 +47,15 @@ module CopyTunerClient
|
|
47
47
|
connect(s3_host) do |http|
|
48
48
|
request = Net::HTTP::Get.new(uri(download_resource))
|
49
49
|
request['If-None-Match'] = @etag
|
50
|
+
log 'Start downloading translations'
|
51
|
+
t = Time.now
|
50
52
|
response = http.request(request)
|
51
|
-
|
53
|
+
t_ms = ((Time.now - t) * 1000).to_i
|
52
54
|
if check response
|
53
|
-
log
|
55
|
+
log "Downloaded translations (#{t_ms}ms)"
|
54
56
|
yield JSON.parse(response.body)
|
55
57
|
else
|
56
|
-
log
|
58
|
+
log "No new translations (#{t_ms}ms)"
|
57
59
|
end
|
58
60
|
|
59
61
|
@etag = response['ETag']
|
@@ -93,6 +93,9 @@ module CopyTunerClient
|
|
93
93
|
# @return [Boolean] disable middleware setting
|
94
94
|
attr_accessor :disable_middleware
|
95
95
|
|
96
|
+
# {before: OtherMiddleware} or {after: OtherMiddleware}
|
97
|
+
attr_accessor :middleware_position
|
98
|
+
|
96
99
|
# @return [Boolean] disable download translation for test enviroment
|
97
100
|
attr_accessor :disable_test_translation
|
98
101
|
|
@@ -105,6 +108,8 @@ module CopyTunerClient
|
|
105
108
|
# @return [Client] instance used to communicate with a CopyTuner Server.
|
106
109
|
attr_accessor :client
|
107
110
|
|
111
|
+
attr_accessor :poller
|
112
|
+
|
108
113
|
# @return [Boolean] To enable inline-translation-mode, set true.
|
109
114
|
attr_accessor :inline_translation
|
110
115
|
|
@@ -230,16 +235,25 @@ module CopyTunerClient
|
|
230
235
|
|
231
236
|
self.client ||= Client.new(to_hash)
|
232
237
|
self.cache ||= Cache.new(client, to_hash)
|
233
|
-
poller = Poller.new(cache, to_hash)
|
234
|
-
process_guard = ProcessGuard.new(cache, poller, to_hash)
|
238
|
+
@poller = Poller.new(cache, to_hash)
|
239
|
+
process_guard = ProcessGuard.new(cache, @poller, to_hash)
|
235
240
|
I18n.backend = I18nBackend.new(cache)
|
236
241
|
|
237
242
|
if enable_middleware?
|
238
243
|
logger.info "Using copytuner sync middleware"
|
239
|
-
|
240
|
-
|
244
|
+
request_sync_options = {:poller => @poller, :cache => cache, :interval => sync_interval, :ignore_regex => sync_ignore_path_regex}
|
245
|
+
if middleware_position.is_a?(Hash) && middleware_position[:before]
|
246
|
+
middleware.insert_before middleware_position[:before], RequestSync, request_sync_options
|
247
|
+
middleware.insert_before middleware_position[:before], CopyTunerClient::CopyrayMiddleware
|
248
|
+
elsif middleware_position.is_a?(Hash) && middleware_position[:after]
|
249
|
+
middleware.insert_after middleware_position[:after], RequestSync, request_sync_options
|
250
|
+
middleware.insert_after middleware_position[:after], CopyTunerClient::CopyrayMiddleware
|
251
|
+
else
|
252
|
+
middleware.use RequestSync, request_sync_options
|
253
|
+
middleware.use CopyTunerClient::CopyrayMiddleware
|
254
|
+
end
|
241
255
|
else
|
242
|
-
logger.info "[[[Warn]]] Not
|
256
|
+
logger.info "[[[Warn]]] Not using copytuner sync middleware" unless middleware
|
243
257
|
end
|
244
258
|
|
245
259
|
@applied = true
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'copy_tuner_client/cache'
|
3
|
+
require 'copy_tuner_client/queue_with_timeout'
|
3
4
|
|
4
5
|
module CopyTunerClient
|
5
6
|
# Starts a background thread that continually resynchronizes with the remote
|
@@ -12,16 +13,34 @@ module CopyTunerClient
|
|
12
13
|
@cache = cache
|
13
14
|
@polling_delay = options[:polling_delay]
|
14
15
|
@logger = options[:logger]
|
15
|
-
@
|
16
|
+
@command_queue = CopyTunerClient::QueueWithTimeout.new
|
17
|
+
@mutex = Mutex.new
|
18
|
+
@thread = nil
|
16
19
|
end
|
17
20
|
|
18
21
|
def start
|
19
|
-
@
|
20
|
-
|
22
|
+
@mutex.synchronize do
|
23
|
+
if @thread.nil?
|
24
|
+
@logger.info 'start poller thread'
|
25
|
+
@thread = Thread.new { poll } or logger.error("Couldn't start poller thread")
|
26
|
+
end
|
27
|
+
end
|
21
28
|
end
|
22
29
|
|
23
30
|
def stop
|
24
|
-
@
|
31
|
+
@mutex.synchronize do
|
32
|
+
@command_queue.uniq_push(:stop)
|
33
|
+
@thread.join if @thread
|
34
|
+
@thread = nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def start_sync
|
39
|
+
@command_queue.uniq_push(:sync)
|
40
|
+
end
|
41
|
+
|
42
|
+
def wait_for_download
|
43
|
+
@cache.wait_for_download
|
25
44
|
end
|
26
45
|
|
27
46
|
private
|
@@ -29,17 +48,19 @@ module CopyTunerClient
|
|
29
48
|
attr_reader :cache, :logger, :polling_delay
|
30
49
|
|
31
50
|
def poll
|
32
|
-
|
51
|
+
loop do
|
33
52
|
cache.sync
|
34
53
|
logger.flush if logger.respond_to?(:flush)
|
35
|
-
|
54
|
+
begin
|
55
|
+
command = @command_queue.pop_with_timeout(polling_delay)
|
56
|
+
break if command == :stop
|
57
|
+
rescue ThreadError
|
58
|
+
# timeout
|
59
|
+
end
|
36
60
|
end
|
61
|
+
@logger.info 'stop poller thread'
|
37
62
|
rescue InvalidApiKey => error
|
38
63
|
logger.error(error.message)
|
39
64
|
end
|
40
|
-
|
41
|
-
def delay
|
42
|
-
sleep(polling_delay)
|
43
|
-
end
|
44
65
|
end
|
45
66
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module CopyTunerClient
|
2
|
+
# https://spin.atomicobject.com/2014/07/07/ruby-queue-pop-timeout/
|
3
|
+
class QueueWithTimeout
|
4
|
+
def initialize
|
5
|
+
@mutex = Mutex.new
|
6
|
+
@queue = []
|
7
|
+
@received = ConditionVariable.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def <<(x)
|
11
|
+
@mutex.synchronize do
|
12
|
+
@queue << x
|
13
|
+
@received.signal
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def uniq_push(x)
|
18
|
+
@mutex.synchronize do
|
19
|
+
unless @queue.member?(x)
|
20
|
+
@queue << x
|
21
|
+
@received.signal
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def pop(non_block = false)
|
27
|
+
pop_with_timeout(non_block ? 0 : nil)
|
28
|
+
end
|
29
|
+
|
30
|
+
def pop_with_timeout(timeout = nil)
|
31
|
+
@mutex.synchronize do
|
32
|
+
if timeout.nil?
|
33
|
+
# wait indefinitely until there is an element in the queue
|
34
|
+
while @queue.empty?
|
35
|
+
@received.wait(@mutex)
|
36
|
+
end
|
37
|
+
elsif @queue.empty? && timeout != 0
|
38
|
+
# wait for element or timeout
|
39
|
+
timeout_time = timeout + Time.now.to_f
|
40
|
+
while @queue.empty? && (remaining_time = timeout_time - Time.now.to_f) > 0
|
41
|
+
@received.wait(@mutex, remaining_time)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
#if we're still empty after the timeout, raise exception
|
45
|
+
raise ThreadError, "queue empty" if @queue.empty?
|
46
|
+
@queue.shift
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -16,10 +16,12 @@ module CopyTunerClient
|
|
16
16
|
# @option options [Cache] :cache agent that should be flushed after each request
|
17
17
|
def initialize(app, options)
|
18
18
|
@app = app
|
19
|
+
@poller = options[:poller]
|
19
20
|
@cache = options[:cache]
|
20
21
|
@interval = options[:interval]
|
21
22
|
@ignore_regex = options[:ignore_regex]
|
22
23
|
@last_synced = options[:last_synced]
|
24
|
+
@first = true
|
23
25
|
end
|
24
26
|
|
25
27
|
attr_accessor :last_synced
|
@@ -30,9 +32,16 @@ module CopyTunerClient
|
|
30
32
|
if /^\/copytuner/ =~ ::Rack::Request.new(env).path_info
|
31
33
|
dup._call(env)
|
32
34
|
else
|
33
|
-
|
35
|
+
first_request = @first
|
36
|
+
if first_request
|
37
|
+
@first = false
|
38
|
+
@cache.download
|
39
|
+
end
|
40
|
+
|
41
|
+
cancel_sync = cancel_sync?(env)
|
34
42
|
response = @app.call(env)
|
35
|
-
@
|
43
|
+
@poller.start_sync unless first_request || cancel_sync
|
44
|
+
|
36
45
|
update_last_synced unless in_interval?
|
37
46
|
response
|
38
47
|
end
|
@@ -140,5 +149,9 @@ module CopyTunerClient
|
|
140
149
|
def update_last_synced
|
141
150
|
@last_synced = Time.now.utc
|
142
151
|
end
|
152
|
+
|
153
|
+
def logger
|
154
|
+
CopyTunerClient.configuration.logger
|
155
|
+
end
|
143
156
|
end
|
144
157
|
end
|
@@ -95,14 +95,4 @@ describe CopyTunerClient::Poller do
|
|
95
95
|
|
96
96
|
expect(logger).to have_received(:flush).at_least_once
|
97
97
|
end
|
98
|
-
|
99
|
-
it "starts from the top-level constant" do
|
100
|
-
poller = build_poller
|
101
|
-
CopyTunerClient.poller = poller
|
102
|
-
poller.stubs(:start)
|
103
|
-
|
104
|
-
CopyTunerClient.start_poller
|
105
|
-
|
106
|
-
expect(poller).to have_received(:start)
|
107
|
-
end
|
108
98
|
end
|
@@ -1,91 +1,81 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe CopyTunerClient::RequestSync do
|
4
|
-
|
4
|
+
let(:poller) { {} }
|
5
5
|
let(:cache) { {} }
|
6
6
|
let(:response) { 'response' }
|
7
7
|
let(:env) { 'env' }
|
8
8
|
let(:app) { stub('app', :call => response) }
|
9
|
-
before
|
10
|
-
|
9
|
+
before do
|
10
|
+
cache.stubs(:flush => nil, :download => nil)
|
11
|
+
poller.stubs(:start_sync => nil)
|
12
|
+
end
|
13
|
+
subject { CopyTunerClient::RequestSync.new(app, :poller => poller, :cache => cache, :interval => 0) }
|
11
14
|
|
12
15
|
it "invokes the upstream app" do
|
13
16
|
result = subject.call(env)
|
14
17
|
expect(app).to have_received(:call).with(env)
|
15
18
|
expect(result).to eq(response)
|
16
19
|
end
|
17
|
-
|
18
|
-
it "flushes defaults" do
|
19
|
-
subject.call(env)
|
20
|
-
expect(cache).to have_received(:flush)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "downloads new copy" do
|
24
|
-
subject.call(env)
|
25
|
-
expect(cache).to have_received(:download)
|
26
|
-
end
|
27
20
|
end
|
28
21
|
|
29
22
|
describe CopyTunerClient::RequestSync, 'serving assets' do
|
30
23
|
let(:env) do
|
31
24
|
{ "PATH_INFO" => '/assets/choper.png' }
|
32
25
|
end
|
26
|
+
let(:poller) { {} }
|
33
27
|
let(:cache) { {} }
|
34
28
|
let(:response) { 'response' }
|
35
29
|
let(:app) { stub('app', :call => response) }
|
36
|
-
before
|
37
|
-
|
30
|
+
before do
|
31
|
+
cache.stubs(:flush => nil, :download => nil)
|
32
|
+
poller.stubs(:start_sync => nil)
|
33
|
+
end
|
34
|
+
subject { CopyTunerClient::RequestSync.new(app, :poller => poller, :cache => cache, :interval => 0) }
|
38
35
|
|
39
|
-
it "
|
36
|
+
it "don't start sync" do
|
40
37
|
subject.call(env)
|
41
|
-
expect(cache).to have_received(:
|
42
|
-
end
|
43
|
-
it "does not download new copy" do
|
38
|
+
expect(cache).to have_received(:download).once
|
44
39
|
subject.call(env)
|
45
|
-
expect(
|
40
|
+
expect(poller).to have_received(:start_sync).never
|
46
41
|
end
|
47
42
|
end
|
48
43
|
|
49
44
|
describe CopyTunerClient::RequestSync do
|
45
|
+
let(:poller) { {} }
|
50
46
|
let(:cache) { {} }
|
51
47
|
let(:response) { 'response' }
|
52
48
|
let(:env) { 'env' }
|
53
49
|
let(:app) { stub('app', :call => response) }
|
54
|
-
|
50
|
+
subject { CopyTunerClient::RequestSync.new(app, :poller => poller, :cache => cache, :interval => 10) }
|
51
|
+
before do
|
52
|
+
cache.stubs(:flush => nil, :download => nil)
|
53
|
+
poller.stubs(:start_sync => nil)
|
54
|
+
end
|
55
55
|
|
56
|
-
context
|
57
|
-
|
58
|
-
it "does not flush defaults" do
|
59
|
-
subject.call(env)
|
60
|
-
expect(cache).to have_received(:flush).never
|
61
|
-
end
|
62
|
-
it "does not download new copy" do
|
56
|
+
context "first request" do
|
57
|
+
it "download" do
|
63
58
|
subject.call(env)
|
64
|
-
expect(cache).to have_received(:download).
|
59
|
+
expect(cache).to have_received(:download).once
|
65
60
|
end
|
66
61
|
end
|
67
62
|
|
68
|
-
context '
|
69
|
-
|
70
|
-
it "flushes defaults" do
|
63
|
+
context 'in interval request' do
|
64
|
+
it "does not start sync for the second time" do
|
71
65
|
subject.call(env)
|
72
|
-
expect(cache).to have_received(:
|
73
|
-
end
|
74
|
-
it "downloads new copy" do
|
66
|
+
expect(cache).to have_received(:download).once
|
75
67
|
subject.call(env)
|
76
|
-
expect(
|
68
|
+
expect(poller).to have_received(:start_sync).never
|
77
69
|
end
|
78
70
|
end
|
79
71
|
|
80
|
-
context
|
81
|
-
|
82
|
-
it "flushes defaults" do
|
72
|
+
context 'over interval request' do
|
73
|
+
it "start sync for the second time" do
|
83
74
|
subject.call(env)
|
84
|
-
expect(cache).to have_received(:
|
85
|
-
|
86
|
-
it "downloads new copy" do
|
75
|
+
expect(cache).to have_received(:download).once
|
76
|
+
subject.last_synced = Time.now - 60
|
87
77
|
subject.call(env)
|
88
|
-
expect(
|
78
|
+
expect(poller).to have_received(:start_sync).once
|
89
79
|
end
|
90
80
|
end
|
91
81
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: copy_tuner_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SonicGarden
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: i18n
|
@@ -263,6 +263,7 @@ files:
|
|
263
263
|
- lib/copy_tuner_client/poller.rb
|
264
264
|
- lib/copy_tuner_client/prefixed_logger.rb
|
265
265
|
- lib/copy_tuner_client/process_guard.rb
|
266
|
+
- lib/copy_tuner_client/queue_with_timeout.rb
|
266
267
|
- lib/copy_tuner_client/rails.rb
|
267
268
|
- lib/copy_tuner_client/request_sync.rb
|
268
269
|
- lib/copy_tuner_client/simple_form_extention.rb
|
@@ -319,7 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
319
320
|
version: '0'
|
320
321
|
requirements: []
|
321
322
|
rubyforge_project:
|
322
|
-
rubygems_version: 2.
|
323
|
+
rubygems_version: 2.7.6
|
323
324
|
signing_key:
|
324
325
|
specification_version: 4
|
325
326
|
summary: Client for the CopyTuner copy management service
|