ghost_reader 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|