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.
@@ -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 = Client.new(config.service)
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.fallback.translate(locale, key, options).tap do |result|
39
- # TODO results which are hashes need to be tracked disaggregated
40
- track({ key => { locale => { 'default' => result } } }) unless result.is_a?(Hash)
41
- end
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
@@ -1,3 +1,3 @@
1
1
  module GhostReader
2
- VERSION = "1.1.2"
2
+ VERSION = "1.1.3"
3
3
  end
@@ -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
- backend = GhostReader::Backend.new
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
- before(:each) do
14
- @translation = 'This is a test.'
15
- @fallback = mock "FallbackBackend"
16
- @fallback.stub!(:translate).and_return(@translation)
17
- @backend = GhostReader::Backend.new(:fallback => @fallback)
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
- @backend.config.fallback.should be(@fallback)
22
- @fallback.should_receive(:translate)
23
- @backend.translate(:en, 'this.is.a.test').should eq(@translation)
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
- @backend.missings = {} # fake init
28
- @backend.translate(:en, 'this.is.a.test')
29
- @backend.missings.keys.should eq(['this.is.a.test'])
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
- @fallback.should_receive(:translate).exactly(1)
34
- 2.times { @backend.translate(:en, 'this.is.a.test').should eq(@translation) }
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 = @backend.send(:symbolize_keys, test_data)
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
- @backend.should respond_to(:available_locales)
62
+ backend.should respond_to(:available_locales)
45
63
 
46
64
  expected = [:en, :de]
47
- @fallback.stub!(:available_locales).and_return(expected)
48
- @backend.available_locales.should eq(expected)
65
+ fallback.stub!(:available_locales).and_return(expected)
66
+ backend.available_locales.should eq(expected)
49
67
 
50
68
  # FIXME
51
- # @backend.send(:memoize_merge!, :it => {'dummay' => 'Dummy'})
52
- # @backend.translate(:it, 'this.is.a.test')
53
- # @backend.available_locales.should eq([:it, :en, :de])
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
- @backend.send(:memoize_merge!, data)
61
- @backend.send(:memoized_lookup).should have_key(:en)
78
+ backend.send(:memoize_merge!, data)
79
+ backend.send(:memoized_lookup).should have_key(:en)
62
80
  # flattend and symbolized
63
- @backend.send(:memoized_lookup)[:en].should have_key(:'this.is.a.test')
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
- @backend.send(:memoize_merge!, data)
87
+ backend.send(:memoize_merge!, data)
70
88
  data = {'en' => {'empty_value' => ''}}
71
- @backend.send(:memoize_merge!, data)
89
+ backend.send(:memoize_merge!, data)
72
90
  data = {'en' => {'' => 'Empty key.'}}
73
- @backend.send(:memoize_merge!, data) # 'interning empty string'
91
+ backend.send(:memoize_merge!, data) # 'interning empty string'
74
92
  data = {'en' => {'value_is_an_array' => %w(what the fuck)}}
75
- @backend.send(:memoize_merge!, data)
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
- @backend.send(:memoize_merge!, data) # 'interning empty string'
83
- @backend.send(:memoized_lookup).should be_empty
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) { GhostReader::Client.new(:api_key => 'some+api_key') }
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
@@ -1 +1,2 @@
1
1
  require 'rubygems'
2
+ require File.expand_path(File.join(%w(.. .. lib ghost_reader)), __FILE__)
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: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 1
9
- - 2
10
- version: 1.1.2
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: 2011-10-11 00:00:00 +02:00
18
+ date: 2012-01-17 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency