redis_template_resolver 0.1.1
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/.gitignore +2 -0
- data/CHANGELOG +6 -0
- data/README.rdoc +84 -0
- data/lib/redis_template_resolver.rb +230 -0
- data/redis_template_resolver.gemspec +28 -0
- data/spec/lib/redis_template_resolver_spec.rb +192 -0
- data/spec/spec_helper.rb +21 -0
- metadata +166 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
= Redis Template Resolver
|
2
|
+
|
3
|
+
== Overview
|
4
|
+
This gem handles remote templates - fetching via http and caching the
|
5
|
+
result locally in the process (first cache level), as well as in a redis
|
6
|
+
instance (second cache level), as well as fallback handling if an error
|
7
|
+
was encountered during the fetching of the template.
|
8
|
+
|
9
|
+
The caches obviously allow faster access to the fetched result, where the
|
10
|
+
second level redis cache allows a centralized, authoritative result to be
|
11
|
+
shared between multiple server processes.
|
12
|
+
|
13
|
+
By default, the process-local cache will be refreshed from the redis cached
|
14
|
+
result every 60 seconds (configurable).
|
15
|
+
|
16
|
+
If an error was encountered during the fetching of the template via http,
|
17
|
+
the library will fall back onto a configurable default template to serve, which
|
18
|
+
will be refreshed after a configurable timeout.
|
19
|
+
|
20
|
+
Once a template has been fetched successfully, it will not time out or be
|
21
|
+
re-fetched. To fetch the template anew, the redis cache entry needs to be
|
22
|
+
removed, which will trigger a new http fetch the next time the first level
|
23
|
+
cache will be refreshed from the second level cache.
|
24
|
+
|
25
|
+
== Configuration
|
26
|
+
|
27
|
+
The following options can be set on the class in a config/initializer/ file:
|
28
|
+
|
29
|
+
+RedisTemplateResolver.default_template+::
|
30
|
+
*mandatory* Body of the default template to use if the remote template cannot
|
31
|
+
be retrieved. Note that this is the actual template, not the path to a
|
32
|
+
template file.
|
33
|
+
+RedisTemplateResolver.redis_connector+::
|
34
|
+
*mandatory* The redis connector to use for storing templates in the second
|
35
|
+
level cache. Note that this is an already configured instance of Redis, not
|
36
|
+
the connection parameters.
|
37
|
+
+RedisTemplateResolver.local_cache_ttl+::
|
38
|
+
The number of seconds between refreshing the in-process cache from redis
|
39
|
+
(defaults to 60).
|
40
|
+
+RedisTemplateResolver.local_cache_negative_ttl+::
|
41
|
+
The number of seconds to wait before retrying to fetch the template via http,
|
42
|
+
if anything went wrong (defaults to 10).
|
43
|
+
+RedisTemplateResolver.http_timeout+::
|
44
|
+
Seconds to wait for the fetch via http to complete (defaults to 5 seconds).
|
45
|
+
+RedisTemplateResolver.template_handler_name+::
|
46
|
+
Name of the template handler that should render the fetched template
|
47
|
+
(defaults to "erb").
|
48
|
+
|
49
|
+
= Usage
|
50
|
+
Create a class that derives from RedisTemplateResolver.
|
51
|
+
|
52
|
+
This class *must* override the following method: +lookup_template_url+. The
|
53
|
+
method will receive the name of the template to be retrieved in +@template_name+
|
54
|
+
and should return a full URL to the template.
|
55
|
+
|
56
|
+
Optionally the following two methods can also be overridden:
|
57
|
+
|
58
|
+
+postprocess_template( template_body )+::
|
59
|
+
Called right after successfully retrieving the template file, which is passed
|
60
|
+
in as an argument. The method should return the template body to be actually
|
61
|
+
stored and used. If you want to perform sanity checks on the returned
|
62
|
+
template, you can raise an +TemplateRejectedError+ exception, which will
|
63
|
+
refuse the retrieved template and pretend that template retrieval failed.
|
64
|
+
+resolver_guard( name, prefix, partial, details)+::
|
65
|
+
If this method is present and returns false or nil, Redis Template Resolver
|
66
|
+
will not handle this template. Return true to process the template resolution
|
67
|
+
with this library.
|
68
|
+
|
69
|
+
In your application controller, add the following line:
|
70
|
+
|
71
|
+
<tt>append_view_path MyRedisTemplateResolver.new</tt>
|
72
|
+
|
73
|
+
where MyRedisTemplateResolver is the name of the class that derives from the
|
74
|
+
RedisTemplateResolver class. Supply a template name with a prefix of "redis:"
|
75
|
+
to route the lookup request to the redis template resolver.
|
76
|
+
|
77
|
+
E.g. to fetch your layouts via http, use the following:
|
78
|
+
|
79
|
+
<tt> layout lambda { |args| "redis:some_template_name}" }</tt>
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
|
@@ -0,0 +1,230 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Looks for templates in a redis cache, The Rails Way(tm).
|
3
|
+
The lookup procedure is as follows:
|
4
|
+
|
5
|
+
1. Determine current consuming application name. If unknown, use default
|
6
|
+
template.
|
7
|
+
|
8
|
+
2. look up template in local cache.
|
9
|
+
If found in local cache, and not expired: Use this template.
|
10
|
+
If found in local cache, but expired: delete from cache, continue to
|
11
|
+
next step.
|
12
|
+
If not found in local cache: continue to next step.
|
13
|
+
|
14
|
+
3. Attempt to retrieve from redis cache.
|
15
|
+
If found, write back to local cache, with a lifetime of
|
16
|
+
+local_cache_ttl+ seconds.
|
17
|
+
If not found, continue to next step.
|
18
|
+
|
19
|
+
4. Attempt to retrieve from remote URL, configured for the consuming
|
20
|
+
application.
|
21
|
+
If successful, write back to redis (indefinitely) and local cache
|
22
|
+
with an expiration of +local_cache_ttl+ seconds.
|
23
|
+
If not successful, continue to next step.
|
24
|
+
|
25
|
+
Note that there is not mutex for the HTTP retrieval, so ALL rails
|
26
|
+
processes could theoretically perform this step at the same time.
|
27
|
+
Fixme: Add Mutex handling with redis.
|
28
|
+
|
29
|
+
5. Write default template to local cache with a lifetime of
|
30
|
+
+local_cache_negative_ttl+ seconds to avoid hammering redis and the
|
31
|
+
remote server.
|
32
|
+
Use the default template.
|
33
|
+
|
34
|
+
NOTE: This class has some stuff hard wired into it that makes it (as is)
|
35
|
+
only useful to look up mustache templates that will be used for a
|
36
|
+
view layout.
|
37
|
+
=end
|
38
|
+
|
39
|
+
class RedisTemplateResolver < ActionView::Resolver
|
40
|
+
class TemplateRejectedError < StandardError ; end
|
41
|
+
|
42
|
+
@@local_cache_ttl = 60 # seconds
|
43
|
+
cattr_accessor :local_cache_ttl
|
44
|
+
|
45
|
+
@@local_cache_negative_ttl = 10 # seconds
|
46
|
+
cattr_accessor :local_cache_negative_ttl
|
47
|
+
|
48
|
+
@@http_timeout = 5 # seconds
|
49
|
+
cattr_accessor :http_timeout
|
50
|
+
|
51
|
+
@@template_handler_name = "erb"
|
52
|
+
cattr_accessor :template_handler_name
|
53
|
+
|
54
|
+
cattr_accessor :cache
|
55
|
+
cattr_accessor :default_template
|
56
|
+
cattr_accessor :redis_connector
|
57
|
+
|
58
|
+
self.caching = false
|
59
|
+
|
60
|
+
=begin rdoc
|
61
|
+
Clears the local cache completely.
|
62
|
+
=end
|
63
|
+
def self.clear_cache
|
64
|
+
@@cache = {}
|
65
|
+
end
|
66
|
+
|
67
|
+
=begin rdoc
|
68
|
+
Called by rails to fetch the template.
|
69
|
+
Returns an array of ActionView::Template instances.
|
70
|
+
If successful, only one template is returned inside the returned array.
|
71
|
+
|
72
|
+
Note that to indicate lookup failure, this function MUST return an empty
|
73
|
+
array!
|
74
|
+
=end
|
75
|
+
def find_templates( name, prefix, partial, details )
|
76
|
+
Rails.logger.debug "Called RedisTemplateResolver"
|
77
|
+
|
78
|
+
return [] unless name.start_with?( "redis:" )
|
79
|
+
if respond_to?( :resolver_guard )
|
80
|
+
return [] unless resolver_guard( name, prefix, partial, details )
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
_, @template_name = name.split(':', 2 )
|
85
|
+
Rails.logger.debug "RedisTemplateResolver fetching template with name: #{@template_name}"
|
86
|
+
|
87
|
+
template_body = fetch_template
|
88
|
+
|
89
|
+
path = ActionView::Resolver::Path.build( name, prefix, nil )
|
90
|
+
handler = ActionView::Template.handler_for_extension( self.template_handler_name )
|
91
|
+
|
92
|
+
template = ActionView::Template.new( template_body, path, handler,
|
93
|
+
:virtual_path => path.virtual,
|
94
|
+
:format => [ :html ],
|
95
|
+
:updated_at => Time.now )
|
96
|
+
return [ template ]
|
97
|
+
end
|
98
|
+
|
99
|
+
protected
|
100
|
+
=begin rdoc
|
101
|
+
Performs the lookup steps described in the class description.
|
102
|
+
Handles the fallbacks on lookup failures in the caches.
|
103
|
+
=end
|
104
|
+
def fetch_template
|
105
|
+
template_body = fetch_template_from_local_cache
|
106
|
+
template_body ||= fetch_template_from_redis
|
107
|
+
template_body ||= fetch_remote_template_and_store
|
108
|
+
template_body ||= store_template_to_local_cache( self.default_template,
|
109
|
+
self.local_cache_negative_ttl )
|
110
|
+
|
111
|
+
return template_body
|
112
|
+
end
|
113
|
+
|
114
|
+
=begin rdoc
|
115
|
+
Triggers the fetching of the template from the remote URL, and writes
|
116
|
+
it back to redis and the local cache on success.
|
117
|
+
|
118
|
+
Returns the template contents as a string, or nil on failure.
|
119
|
+
=end
|
120
|
+
def fetch_remote_template_and_store
|
121
|
+
template_body = fetch_template_from_url
|
122
|
+
|
123
|
+
if template_body
|
124
|
+
store_template_to_local_cache( template_body )
|
125
|
+
store_template_to_redis( template_body )
|
126
|
+
end
|
127
|
+
|
128
|
+
return template_body
|
129
|
+
end
|
130
|
+
|
131
|
+
=begin rdoc
|
132
|
+
Returns the redis key used for template lookups.
|
133
|
+
=end
|
134
|
+
def redis_key
|
135
|
+
return "rlt:#{@template_name}"
|
136
|
+
end
|
137
|
+
|
138
|
+
=begin rdoc
|
139
|
+
Performs the HTTP retrieval of a template from the remote url configured.
|
140
|
+
Validates the consuming application name and encoding of the returned
|
141
|
+
template, if the HTTP request was answered by a 200 code.
|
142
|
+
|
143
|
+
Returns the template contents as a string, or nil if any of the steps fail.
|
144
|
+
=end
|
145
|
+
def fetch_template_from_url
|
146
|
+
layout_url = lookup_template_url
|
147
|
+
return nil if layout_url == nil
|
148
|
+
|
149
|
+
Rails.logger.info "Fetching remote template from #{layout_url.inspect}"
|
150
|
+
|
151
|
+
response = HTTParty.get( layout_url, :timeout => self.http_timeout )
|
152
|
+
response_body = response.body
|
153
|
+
|
154
|
+
Rails.logger.info "Got remote template response code #{response.code} with body #{response_body.inspect}"
|
155
|
+
|
156
|
+
return nil if response.code == 404
|
157
|
+
|
158
|
+
response_body = postprocess_template( response_body ) if respond_to?( :postprocess_template )
|
159
|
+
|
160
|
+
return response_body
|
161
|
+
|
162
|
+
rescue Timeout::Error, SocketError, Errno::ECONNREFUSED, TemplateRejectedError => e
|
163
|
+
Rails.logger.error e.message
|
164
|
+
return nil
|
165
|
+
end
|
166
|
+
|
167
|
+
def lookup_template_url
|
168
|
+
# :nocov:
|
169
|
+
raise "Override me! Expect to find the template name in @template_name"
|
170
|
+
# :nocov:
|
171
|
+
end
|
172
|
+
|
173
|
+
=begin rdoc
|
174
|
+
Returns the template contents from the local cache, checking the expiration
|
175
|
+
time as it does so.
|
176
|
+
|
177
|
+
Returns the template content on success, or nil on failure.
|
178
|
+
|
179
|
+
Also removes the template from the cache if the entry is expired.
|
180
|
+
=end
|
181
|
+
def fetch_template_from_local_cache
|
182
|
+
@@cache ||= {}
|
183
|
+
|
184
|
+
return nil unless @@cache[@template_name]
|
185
|
+
|
186
|
+
if @@cache[@template_name][:expiration].to_i <= Time.now.to_i
|
187
|
+
Rails.logger.debug( "Local cache entry is too old, removing!" )
|
188
|
+
@@cache.delete( @template_name )
|
189
|
+
return nil
|
190
|
+
end
|
191
|
+
|
192
|
+
return @@cache[@template_name][:template]
|
193
|
+
end
|
194
|
+
|
195
|
+
=begin rdoc
|
196
|
+
Fetches the template from redis, and writes the retrieved value to the
|
197
|
+
local cache on success.
|
198
|
+
|
199
|
+
Returns the template contents as a string, or nil on failure.
|
200
|
+
=end
|
201
|
+
def fetch_template_from_redis
|
202
|
+
result = self.redis_connector.get( redis_key )
|
203
|
+
store_template_to_local_cache( result ) if result
|
204
|
+
|
205
|
+
return result
|
206
|
+
end
|
207
|
+
|
208
|
+
=begin rdoc
|
209
|
+
Does the actual storing of the template contents in the local cache.
|
210
|
+
The ttl value defaults to the +local_cache_ttl+ and is interpreted as being
|
211
|
+
given in seconds.
|
212
|
+
|
213
|
+
Returns the passed in template body.
|
214
|
+
=end
|
215
|
+
def store_template_to_local_cache( template_body, ttl = self.local_cache_ttl )
|
216
|
+
@@cache ||= {}
|
217
|
+
|
218
|
+
@@cache[@template_name] = { :template => template_body,
|
219
|
+
:expiration => Time.now.to_i + ttl }
|
220
|
+
|
221
|
+
return template_body
|
222
|
+
end
|
223
|
+
|
224
|
+
=begin rdoc
|
225
|
+
Stores the given template body to the redis cache.
|
226
|
+
=end
|
227
|
+
def store_template_to_redis( template_body )
|
228
|
+
return self.redis_connector.set( redis_key, template_body )
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "redis_template_resolver"
|
3
|
+
s.version = "0.1.1"
|
4
|
+
s.homepage = "https://github.com/psd/redis_template_resolver"
|
5
|
+
s.summary = "A template resolver for rails that retrieves templates via http and caches them in redis"
|
6
|
+
|
7
|
+
s.description = <<-EOS
|
8
|
+
A template resolver for rails 3.2. It retrieves templates via HTTP
|
9
|
+
and stores them in a redis instance, as well as in local caches.
|
10
|
+
EOS
|
11
|
+
|
12
|
+
s.authors = [ "ProSiebenSat.1 Ditital, Central Systems Architecture" ]
|
13
|
+
s.email = ["sven.riedel@prosiebensat1digital.de"]
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
|
19
|
+
s.add_runtime_dependency( "redis", "> 3.0.0" )
|
20
|
+
s.add_runtime_dependency( "httparty", "> 0.8.0" )
|
21
|
+
s.add_runtime_dependency( "rails", "~> 3.2.6" )
|
22
|
+
s.add_runtime_dependency( "activesupport", ">3.2.0" )
|
23
|
+
|
24
|
+
s.add_development_dependency( "rspec", ">2.10.0" )
|
25
|
+
s.add_development_dependency( "simplecov" )
|
26
|
+
s.add_development_dependency( "timecop" )
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class ExampleClass < RedisTemplateResolver
|
4
|
+
def lookup_template_url
|
5
|
+
return "http://www.example.com/some/path"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe ExampleClass do
|
10
|
+
|
11
|
+
let( :dummy_remote_template ) { "dummy template {{{flash}}} {{{body}}} {{{head}}} {{{headline}}} {{{title}}}" }
|
12
|
+
let( :dummy_remote_template_with_html_escaped_tags ) { "dummy template {{flash}} {{body}} {{head}} {{headline}} {{title}}" }
|
13
|
+
|
14
|
+
let( :fake_httparty_response ) do
|
15
|
+
OpenStruct.new( :code => 200, :body => dummy_remote_template )
|
16
|
+
end
|
17
|
+
|
18
|
+
let( :fake_httparty_response_with_html_escaped_tags ) do
|
19
|
+
OpenStruct.new( :code => 200, :body => dummy_remote_template_with_html_escaped_tags )
|
20
|
+
end
|
21
|
+
|
22
|
+
let( :redis_connector ) do
|
23
|
+
double( Redis, :get => nil, :set => nil )
|
24
|
+
end
|
25
|
+
|
26
|
+
before( :each ) do
|
27
|
+
described_class.redis_connector = redis_connector
|
28
|
+
described_class.clear_cache
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#find_templates" do
|
32
|
+
it "should return an empty array if the template isn't a layout" do
|
33
|
+
result = subject.find_templates( "foo", "not_a_layout", "", {} )
|
34
|
+
result.should == []
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return an empty array if the template name doesn't start with redis:" do
|
38
|
+
result = subject.find_templates( "not_redis:template", "layouts", "", {} )
|
39
|
+
result.should == []
|
40
|
+
end
|
41
|
+
|
42
|
+
context "with a guard function supplied" do
|
43
|
+
it 'should abort processing if the guard function returns false' do
|
44
|
+
ExampleClass.any_instance.stub( :resolver_guard ).and_return( false )
|
45
|
+
result = subject.find_templates( "redis:template", "layouts", "", {} )
|
46
|
+
result.should == []
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should continue processing if the guard function returns true' do
|
50
|
+
HTTParty.stub!( :get => fake_httparty_response )
|
51
|
+
ExampleClass.any_instance.stub( :resolver_guard ).and_return( true )
|
52
|
+
result = subject.find_templates( "redis:template", "layouts", "", {} )
|
53
|
+
result.first.source.should == dummy_remote_template
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when retrieving a relevant template" do
|
58
|
+
let( :name ) { "redis:kabeleins_local" }
|
59
|
+
let( :prefix ) { "layouts" }
|
60
|
+
let( :partial ) { "" }
|
61
|
+
let( :details ) { {} }
|
62
|
+
|
63
|
+
context "when retrieving the template from the local cache" do
|
64
|
+
it "should fall back when the template is unknown" do
|
65
|
+
described_class.redis_connector.should_receive( :get ).once.and_return( nil )
|
66
|
+
subject.find_templates( name, prefix, partial, details )
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should fall back when the consuming application is unknown" do
|
70
|
+
described_class.redis_connector.should_receive( :get ).once.and_return( nil )
|
71
|
+
subject.find_templates( "redis:no_such_application", prefix, partial, details )
|
72
|
+
end
|
73
|
+
|
74
|
+
context "if the template in the local cache is too old" do
|
75
|
+
before( :each ) do
|
76
|
+
Timecop.travel( described_class.local_cache_ttl.seconds.ago ) do
|
77
|
+
HTTParty.stub!( :get ).and_return( fake_httparty_response )
|
78
|
+
subject.find_templates( name, prefix, partial, details )
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should fall back and remove the template if it is older than the local cache TTL" do
|
83
|
+
described_class.redis_connector.should_receive( :get ).once.and_return( nil )
|
84
|
+
subject.find_templates( name, prefix, partial, details )
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "if the template is in the local cache less than the local cache TTL" do
|
89
|
+
before( :each ) do
|
90
|
+
target_time = described_class.local_cache_ttl - 2
|
91
|
+
Timecop.travel( target_time.seconds.ago ) do
|
92
|
+
HTTParty.stub!( :get => fake_httparty_response )
|
93
|
+
subject.find_templates( name, prefix, partial, details )
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should use the found template and not fall back" do
|
98
|
+
redis_connector.should_not_receive( :get )
|
99
|
+
result = subject.find_templates( name, prefix, partial, details )
|
100
|
+
result.first.source.should == dummy_remote_template
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "when the template cannot be found in the local cache" do
|
106
|
+
before( :each ) do
|
107
|
+
described_class.clear_cache
|
108
|
+
end
|
109
|
+
|
110
|
+
context "if a template could be found in the redis cache" do
|
111
|
+
before( :each ) do
|
112
|
+
redis_connector.stub!( :get ).and_return( dummy_remote_template )
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should store the template in the local cache for 60 seconds" do
|
116
|
+
subject.find_templates( name, prefix, partial, details )
|
117
|
+
described_class.cache.should have_key( "kabeleins_local" )
|
118
|
+
|
119
|
+
cache_entry = described_class.cache["kabeleins_local"]
|
120
|
+
cache_entry.should have_key( :template )
|
121
|
+
cache_entry.should have_key( :expiration )
|
122
|
+
cache_entry[:template].should == dummy_remote_template
|
123
|
+
expected_expiration_time = Time.now.to_i + described_class.local_cache_ttl
|
124
|
+
cache_entry[:expiration].should be_between( expected_expiration_time - 2,
|
125
|
+
expected_expiration_time )
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should not fall back to http" do
|
129
|
+
HTTParty.should_not_receive( :get )
|
130
|
+
subject.find_templates( name, prefix, partial, details )
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
context "if no template could be found in the redis cache" do
|
136
|
+
before( :each ) do
|
137
|
+
redis_connector.stub!( :get )
|
138
|
+
end
|
139
|
+
|
140
|
+
context "if the template could be retrieved via HTTP" do
|
141
|
+
before( :each ) do
|
142
|
+
HTTParty.stub!( :get => fake_httparty_response )
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should store the template in redis" do
|
146
|
+
redis_connector.should_receive( :set ).once.with( "rlt:kabeleins_local",
|
147
|
+
dummy_remote_template )
|
148
|
+
subject.find_templates( name, prefix, partial, details )
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should store the template in the local cache" do
|
152
|
+
subject.find_templates( name, prefix, partial, details )
|
153
|
+
described_class.cache.should have_key( "kabeleins_local" )
|
154
|
+
|
155
|
+
cache_entry = described_class.cache["kabeleins_local"]
|
156
|
+
cache_entry.should have_key( :template )
|
157
|
+
cache_entry.should have_key( :expiration )
|
158
|
+
cache_entry[:template].should == dummy_remote_template
|
159
|
+
expected_expiration_time = Time.now.to_i + described_class.local_cache_ttl
|
160
|
+
cache_entry[:expiration].should be_between( expected_expiration_time - 2,
|
161
|
+
expected_expiration_time )
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context "if the template could not be retrieved via HTTP" do
|
166
|
+
before( :each ) do
|
167
|
+
HTTParty.stub!( :get ).and_raise( Timeout::Error )
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should use the default template" do
|
171
|
+
result = subject.find_templates( name, prefix, partial, details )
|
172
|
+
result.first.source.should == described_class.default_template
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should store the default template in the local cache for 10 seconds" do
|
176
|
+
subject.find_templates( name, prefix, partial, details )
|
177
|
+
described_class.cache.should have_key( "kabeleins_local" )
|
178
|
+
|
179
|
+
cache_entry = described_class.cache["kabeleins_local"]
|
180
|
+
cache_entry.should have_key( :template )
|
181
|
+
cache_entry.should have_key( :expiration )
|
182
|
+
cache_entry[:template].should == described_class.default_template
|
183
|
+
expected_expiration_time = Time.now.to_i + described_class.local_cache_negative_ttl
|
184
|
+
cache_entry[:expiration].should be_between( expected_expiration_time - 2,
|
185
|
+
expected_expiration_time )
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'redis'
|
3
|
+
require 'rails'
|
4
|
+
require 'action_view'
|
5
|
+
require 'active_support/core_ext/numeric/time'
|
6
|
+
require 'httparty'
|
7
|
+
require 'timecop'
|
8
|
+
require 'ostruct'
|
9
|
+
|
10
|
+
SimpleCov.start
|
11
|
+
require_relative "../lib/redis_template_resolver"
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.order = "random"
|
15
|
+
config.mock_with :rspec
|
16
|
+
|
17
|
+
config.before( :each ) do
|
18
|
+
logger = double( "Logger" ).as_null_object
|
19
|
+
Rails.logger = logger
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redis_template_resolver
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- ProSiebenSat.1 Ditital, Central Systems Architecture
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redis
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>'
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>'
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.0.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: httparty
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>'
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.8.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>'
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.8.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rails
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 3.2.6
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.2.6
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: activesupport
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>'
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 3.2.0
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>'
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 3.2.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>'
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.10.0
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>'
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 2.10.0
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: simplecov
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: timecop
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: ! " A template resolver for rails 3.2. It retrieves templates via
|
127
|
+
HTTP \n and stores them in a redis instance, as well as in local caches. \n"
|
128
|
+
email:
|
129
|
+
- sven.riedel@prosiebensat1digital.de
|
130
|
+
executables: []
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- .gitignore
|
135
|
+
- CHANGELOG
|
136
|
+
- README.rdoc
|
137
|
+
- lib/redis_template_resolver.rb
|
138
|
+
- redis_template_resolver.gemspec
|
139
|
+
- spec/lib/redis_template_resolver_spec.rb
|
140
|
+
- spec/spec_helper.rb
|
141
|
+
homepage: https://github.com/psd/redis_template_resolver
|
142
|
+
licenses: []
|
143
|
+
post_install_message:
|
144
|
+
rdoc_options: []
|
145
|
+
require_paths:
|
146
|
+
- lib
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ! '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
none: false
|
155
|
+
requirements:
|
156
|
+
- - ! '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
requirements: []
|
160
|
+
rubyforge_project:
|
161
|
+
rubygems_version: 1.8.23
|
162
|
+
signing_key:
|
163
|
+
specification_version: 3
|
164
|
+
summary: A template resolver for rails that retrieves templates via http and caches
|
165
|
+
them in redis
|
166
|
+
test_files: []
|