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.
@@ -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