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.
@@ -0,0 +1,2 @@
1
+ .*.sw[po]
2
+ coverage/
@@ -0,0 +1,6 @@
1
+ v0.1.1
2
+ - Fix dependency in gemspec
3
+ - Add CHANGELOG file
4
+
5
+ v0.1.0
6
+ - Initial independent release
@@ -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
@@ -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: []