ghost_reader 1.1.2 → 1.1.3
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.
- data/lib/ghost_reader/backend.rb +19 -7
- data/lib/ghost_reader/version.rb +1 -1
- data/spec/ghost_reader/backend_spec.rb +85 -30
- data/spec/ghost_reader/client_spec.rb +6 -3
- data/spec/spec_helper.rb +1 -0
- metadata +4 -4
data/lib/ghost_reader/backend.rb
CHANGED
@@ -9,6 +9,9 @@ module GhostReader
|
|
9
9
|
|
10
10
|
attr_accessor :config, :missings
|
11
11
|
|
12
|
+
# exposed for testing & debugging
|
13
|
+
attr_reader :retriever, :reporter
|
14
|
+
|
12
15
|
# for options see code of default_config
|
13
16
|
def initialize(conf={})
|
14
17
|
self.config = OpenStruct.new(default_config.merge(conf))
|
@@ -16,7 +19,7 @@ module GhostReader
|
|
16
19
|
config.logger = Logger.new(config.logfile || STDOUT)
|
17
20
|
config.logger.level = config.log_level || Logger::WARN
|
18
21
|
config.service[:logger] ||= config.logger
|
19
|
-
config.client
|
22
|
+
config.client ||= Client.new(config.service)
|
20
23
|
unless config.no_auto_spawn
|
21
24
|
config.logger.debug "GhostReader spawning agents."
|
22
25
|
spawn_retriever
|
@@ -35,17 +38,26 @@ module GhostReader
|
|
35
38
|
# this won't be called if memoize kicks in
|
36
39
|
def lookup(locale, key, scope = [], options = {})
|
37
40
|
raise 'no fallback given' if config.fallback.nil?
|
38
|
-
config.
|
39
|
-
|
40
|
-
|
41
|
-
|
41
|
+
config.logger.debug "lookup: #{locale} #{key} #{scope.inspect} #{options.inspect}"
|
42
|
+
|
43
|
+
result = config.fallback.translate(locale, key, options)
|
44
|
+
config.logger.debug "fallback result: #{result.inspect}"
|
45
|
+
return result
|
46
|
+
rescue Exception => ex
|
47
|
+
config.logger.debug "fallback.translate raised exception: #{ex}"
|
48
|
+
ensure # make sure everything is tracked
|
49
|
+
# TODO results which are hashes need to be tracked disaggregated
|
50
|
+
track({ key => { locale.to_s => { 'default' => result } } }) unless result.is_a?(Hash)
|
51
|
+
ex.nil? ? result : raise(ex)
|
42
52
|
end
|
43
53
|
|
44
54
|
def track(missings)
|
45
55
|
return if self.missings.nil? # not yet initialized
|
56
|
+
config.logger.debug "tracking: #{missings.inspect}"
|
46
57
|
self.missings.deep_merge!(missings)
|
47
58
|
end
|
48
59
|
|
60
|
+
# data, e.g. {'en' => {'this' => {'is' => {'a' => {'test' => 'This is a test.'}}}}}
|
49
61
|
def memoize_merge!(data, options={ :method => :merge! })
|
50
62
|
flattend = flatten_translations_for_all_locales(data)
|
51
63
|
symbolized_flattend = symbolize_keys(flattend)
|
@@ -55,7 +67,7 @@ module GhostReader
|
|
55
67
|
# performs initial and incremental requests
|
56
68
|
def spawn_retriever
|
57
69
|
config.logger.debug "Spawning retriever."
|
58
|
-
Thread.new do
|
70
|
+
@retriever = Thread.new do
|
59
71
|
begin
|
60
72
|
config.logger.debug "Performing initial request."
|
61
73
|
response = config.client.initial_request
|
@@ -88,7 +100,7 @@ module GhostReader
|
|
88
100
|
# performs reporting requests
|
89
101
|
def spawn_reporter
|
90
102
|
config.logger.debug "Spawning reporter."
|
91
|
-
Thread.new do
|
103
|
+
@reporter = Thread.new do
|
92
104
|
until false
|
93
105
|
begin
|
94
106
|
sleep config.report_interval
|
data/lib/ghost_reader/version.rb
CHANGED
@@ -2,88 +2,143 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe GhostReader::Backend do
|
4
4
|
|
5
|
+
let(:dev_null) { File.new('/dev/null', 'w') }
|
6
|
+
|
7
|
+
let(:client) do
|
8
|
+
mock("Client").tap do |client|
|
9
|
+
response = {'en' => {'this' => {'is' => {'a' => {'test' => 'This is a test.'}}}}}
|
10
|
+
client.stub!(:initial_request).and_return(:data => response)
|
11
|
+
client.stub!(:incremental_request).and_return(:data => response)
|
12
|
+
client.stub!(:reporting_request)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
5
16
|
context 'on class level' do
|
6
17
|
it 'should nicely initialize' do
|
7
|
-
|
18
|
+
GhostReader::Backend.new( :logfile => dev_null ).should be_instance_of(GhostReader::Backend)
|
8
19
|
end
|
9
20
|
end
|
10
21
|
|
11
22
|
context 'Backend set up with fallback' do
|
12
23
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
24
|
+
let(:translation) { 'This is a test.' }
|
25
|
+
|
26
|
+
let(:fallback) do
|
27
|
+
mock("FallbackBackend").tap do |fallback|
|
28
|
+
fallback.stub!(:translate).and_return(translation)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
let(:backend) do
|
33
|
+
GhostReader::Backend.new( :logfile => dev_null,
|
34
|
+
:log_level => Logger::DEBUG,
|
35
|
+
:fallback => fallback )
|
18
36
|
end
|
19
37
|
|
20
38
|
it 'should use the given fallback' do
|
21
|
-
|
22
|
-
|
23
|
-
|
39
|
+
backend.config.fallback.should be(fallback)
|
40
|
+
fallback.should_receive(:translate)
|
41
|
+
backend.translate(:en, 'this.is.a.test').should eq(translation)
|
24
42
|
end
|
25
43
|
|
26
44
|
it 'should track missings' do
|
27
|
-
|
28
|
-
|
29
|
-
|
45
|
+
backend.missings = {} # fake init
|
46
|
+
backend.translate(:en, 'this.is.a.test')
|
47
|
+
backend.missings.keys.should eq(['this.is.a.test'])
|
30
48
|
end
|
31
49
|
|
32
50
|
it 'should use memoization' do
|
33
|
-
|
34
|
-
2.times {
|
51
|
+
fallback.should_receive(:translate).exactly(1)
|
52
|
+
2.times { backend.translate(:en, 'this.is.a.test').should eq(translation) }
|
35
53
|
end
|
36
54
|
|
37
55
|
it 'should symbolize keys' do
|
38
56
|
test_data = { "one" => "1", "two" => "2"}
|
39
|
-
result =
|
57
|
+
result = backend.send(:symbolize_keys, test_data)
|
40
58
|
result.has_key?(:one).should be_true
|
41
59
|
end
|
42
60
|
|
43
61
|
it 'should nicely respond to available_locales' do
|
44
|
-
|
62
|
+
backend.should respond_to(:available_locales)
|
45
63
|
|
46
64
|
expected = [:en, :de]
|
47
|
-
|
48
|
-
|
65
|
+
fallback.stub!(:available_locales).and_return(expected)
|
66
|
+
backend.available_locales.should eq(expected)
|
49
67
|
|
50
68
|
# FIXME
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
69
|
+
# backend.send(:memoize_merge!, :it => {'dummay' => 'Dummy'})
|
70
|
+
# backend.translate(:it, 'this.is.a.test')
|
71
|
+
# backend.available_locales.should eq([:it, :en, :de])
|
54
72
|
end
|
55
73
|
|
56
74
|
context 'nicely merge data into memoized_hash' do
|
57
75
|
|
58
76
|
it 'should work with valid data' do
|
59
77
|
data = {'en' => {'this' => {'is' => {'a' => {'test' => 'This is a test.'}}}}}
|
60
|
-
|
61
|
-
|
78
|
+
backend.send(:memoize_merge!, data)
|
79
|
+
backend.send(:memoized_lookup).should have_key(:en)
|
62
80
|
# flattend and symbolized
|
63
|
-
|
81
|
+
backend.send(:memoized_lookup)[:en].should have_key(:'this.is.a.test')
|
64
82
|
end
|
65
83
|
|
66
84
|
it 'should handle weird data gracefully' do
|
67
85
|
expect do
|
68
86
|
data = {'en' => {'value_is_an_hash' => {'1st' => 'bla', '2nd' => 'blub'}}}
|
69
|
-
|
87
|
+
backend.send(:memoize_merge!, data)
|
70
88
|
data = {'en' => {'empty_value' => ''}}
|
71
|
-
|
89
|
+
backend.send(:memoize_merge!, data)
|
72
90
|
data = {'en' => {'' => 'Empty key.'}}
|
73
|
-
|
91
|
+
backend.send(:memoize_merge!, data) # 'interning empty string'
|
74
92
|
data = {'en' => {'value_is_an_array' => %w(what the fuck)}}
|
75
|
-
|
93
|
+
backend.send(:memoize_merge!, data)
|
76
94
|
end.to_not raise_error
|
77
95
|
end
|
78
96
|
|
79
97
|
# key should not be empty but if it is...
|
80
98
|
it 'should not raise error when key is empty' do
|
81
99
|
data = {'en' => {'' => 'Empty key.'}}
|
82
|
-
|
83
|
-
|
100
|
+
backend.send(:memoize_merge!, data) # 'interning empty string'
|
101
|
+
backend.send(:memoized_lookup).should be_empty
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'GhostReader set up without fallback' do
|
109
|
+
let(:backend) { GhostReader::Backend.new(:logfile => dev_null) }
|
110
|
+
|
111
|
+
it 'should raise an error' do
|
112
|
+
expect { backend.translate(:de, :asdf) }.to raise_error('no fallback given')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'GhostReader set up with raising fallback' do
|
117
|
+
let(:fallback) do
|
118
|
+
mock("FallbackBackend").tap do |fallback|
|
119
|
+
fallback.stub!(:translate) do
|
120
|
+
raise 'missing translation'
|
121
|
+
end
|
84
122
|
end
|
123
|
+
end
|
124
|
+
|
125
|
+
let(:backend) do
|
126
|
+
GhostReader::Backend.new( :logfile => dev_null,
|
127
|
+
:log_level => Logger::DEBUG,
|
128
|
+
:fallback => fallback,
|
129
|
+
:client => client )
|
130
|
+
end
|
85
131
|
|
132
|
+
it 'should behave nicely' do
|
133
|
+
expect { backend.translate(:de, :asdf) }.to raise_error('missing translation')
|
86
134
|
end
|
87
135
|
|
136
|
+
it 'should track lookups which raise exceptions' do
|
137
|
+
backend.retriever.should be_alive
|
138
|
+
backend.missings.should_not be_nil
|
139
|
+
expect { backend.translate(:de, :asdf) }.to raise_error('missing translation')
|
140
|
+
backend.missings.should_not be_empty
|
141
|
+
end
|
88
142
|
end
|
143
|
+
|
89
144
|
end
|
@@ -1,6 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require File.expand_path(File.join(%w(.. .. .. lib ghost_reader)), __FILE__)
|
3
|
-
|
4
2
|
|
5
3
|
Excon.mock = true
|
6
4
|
|
@@ -12,6 +10,8 @@ end
|
|
12
10
|
|
13
11
|
describe GhostReader::Client do
|
14
12
|
|
13
|
+
let(:dev_null) { File.new('/dev/null', 'w') }
|
14
|
+
|
15
15
|
context 'on class level' do
|
16
16
|
it 'should nicely initialize' do
|
17
17
|
GhostReader::Client.new.should be_an_instance_of(GhostReader::Client)
|
@@ -20,7 +20,10 @@ describe GhostReader::Client do
|
|
20
20
|
|
21
21
|
context 'a initialized client' do
|
22
22
|
|
23
|
-
let(:client)
|
23
|
+
let(:client) do
|
24
|
+
GhostReader::Client.new( :logfile => dev_null,
|
25
|
+
:api_key => 'some+api_key' )
|
26
|
+
end
|
24
27
|
|
25
28
|
before(:each) { Excon.kill_stubs! }
|
26
29
|
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ghost_reader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 1.1.
|
9
|
+
- 3
|
10
|
+
version: 1.1.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Phil Hofmann
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-01-17 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|