Permalink
Browse files

Added API response caching via Rack::Cache.

  • Loading branch information...
1 parent 8a64fbf commit 883e1f3071389b7a281441cc79ee576fc415de5a Derek Lindahl committed Apr 18, 2012
Showing with 97 additions and 10 deletions.
  1. +22 −1 README.md
  2. +11 −9 frenetic.gemspec
  3. +7 −0 lib/frenetic.rb
  4. +23 −0 lib/frenetic/configuration.rb
  5. +34 −0 spec/lib/frenetic/configuration_spec.rb
View
@@ -158,6 +158,26 @@ MyAPI = Frenetic.new(
```
+### Response Caching
+
+If configured to do so, Frenetic will autotmatically cache appropriate responses
+through [Rack::Cache][rach_cache]. Only on-disk stores are supported right now.
+
+Add the following `Rack::Cache` configuration options when initializing Frenetic:
+
+```ruby
+MyAPI = Frenetic.new(
+ ...
+ 'cache' => {
+ 'metastore' => 'file:/path/to/where/you/want/to/store/files/meta',
+ 'entitystore' => 'file:/path/to/where/you/want/to/store/files/meta'
+ }
+)
+```
+
+The `cache` options are passed directly to `Rack::Cache`, so anything it
+supports can be added to the Hash.
+
### Making Requests
@@ -249,4 +269,5 @@ more readable:
[hal_json]: http://stateless.co/hal_specification.html
[spire.io]: http://api.spire.io/
[roar]: https://github.com/apotonick/roar
-[faraday]: https://github.com/technoweenie/faraday
+[faraday]: https://github.com/technoweenie/faraday
+[rack_cache]: https://github.com/rtomayko/rack-cache
View
@@ -15,14 +15,16 @@ Gem::Specification.new do |gem|
gem.require_paths = ["lib"]
gem.version = Frenetic::VERSION
- gem.add_dependency 'faraday', '~> 0.8.0.rc2'
- gem.add_dependency 'addressable', '~> 2.2.7'
- gem.add_dependency 'patron', '~> 0.4.18'
+ gem.add_dependency 'faraday', '~> 0.8.0.rc2'
+ gem.add_dependency 'faraday_middleware', '~> 0.8.6'
+ gem.add_dependency 'rack-cache', '~> 1.1'
+ gem.add_dependency 'addressable', '~> 2.2.7'
+ gem.add_dependency 'patron', '~> 0.4.18'
- gem.add_development_dependency 'guard-spork', '~> 0.6.0'
- gem.add_development_dependency 'guard-rspec', '~> 0.7.0'
- gem.add_development_dependency 'rspec', '~> 2.9.0'
- gem.add_development_dependency 'bourne', '~> 1.1.2'
- gem.add_development_dependency 'webmock', '~> 1.8.6'
- gem.add_development_dependency 'vcr', '~> 2.0.1'
+ gem.add_development_dependency 'guard-spork', '~> 0.6.0'
+ gem.add_development_dependency 'guard-rspec', '~> 0.7.0'
+ gem.add_development_dependency 'rspec', '~> 2.9.0'
+ gem.add_development_dependency 'bourne', '~> 1.1.2'
+ gem.add_development_dependency 'webmock', '~> 1.8.6'
+ gem.add_development_dependency 'vcr', '~> 2.0.1'
end
View
@@ -1,6 +1,8 @@
require 'addressable/uri'
require 'patron' # Needed to prevent https://github.com/technoweenie/faraday/issues/140
require 'faraday'
+require 'faraday_middleware'
+require 'rack-cache'
require "frenetic/configuration"
require "frenetic/hal_json"
@@ -26,6 +28,11 @@ def initialize( config = {} )
@connection = Faraday.new( config ) do |builder|
builder.use HalJson
builder.request :basic_auth, config[:username], config[:password]
+
+ if config[:cache]
+ builder.use FaradayMiddleware::RackCompatible, Rack::Cache::Context, config[:cache]
+ end
+
builder.adapter :patron
end
end
@@ -23,6 +23,7 @@ def initialize( custom_config = {} )
super()
configure_user_agent
+ configure_cache
validate
end
@@ -39,10 +40,32 @@ def configure_user_agent
end
end
+ def configure_cache
+ if self[:cache]
+ ignore_headers = (self[:cache][:ignore_headers] || '').split(' ')
+
+ self[:cache][:ignore_headers] = (ignore_headers + %w[Set-Cookie X-Content-Digest]).uniq
+ end
+ end
+
def validate
unless self[:url]
raise ConfigurationError, "No API URL defined!"
end
+ if self[:cache]
+ raise( ConfigurationError, "No cache :metastore defined!" ) if self[:cache][:metastore].to_s == ""
+ raise( ConfigurationError, "No cache :entitystore defined!" ) if self[:cache][:entitystore].to_s == ""
+ raise( ConfigurationError, "Required cache header filters are missing!" ) if missing_required_headers?
+ end
+ end
+
+ def missing_required_headers?
+ return true if self[:cache][:ignore_headers].empty?
+
+ header_set = self[:cache][:ignore_headers]
+ custom_headers = header_set - %w[Set-Cookie X-Content-Digest]
+
+ header_set == custom_headers
end
# TODO: Is this even being used?
@@ -70,6 +70,40 @@
it "should set a User Agent request header" do
subject[:headers][:user_agent].should =~ %r{Frenetic v.+; \S+$}
end
+
+ context "which includes incorrect cache settings" do
+ before { Frenetic::Configuration.any_instance.stubs(:configure_cache).returns(nil) }
+
+ it "should raise a configuration error for a missing :metastore" do
+ expect {
+ Frenetic::Configuration.new('url' => 'http://example.org', 'cache' => {} )
+ }.to raise_error(
+ Frenetic::Configuration::ConfigurationError, "No cache :metastore defined!"
+ )
+ end
+
+ it "should raise a configuration error for a missing :entitystore" do
+ expect {
+ Frenetic::Configuration.new('url' => 'http://example.org', 'cache' => { 'metastore' => 'foo' } )
+ }.to raise_error(
+ Frenetic::Configuration::ConfigurationError, "No cache :entitystore defined!"
+ )
+ end
+
+ it "should raise a configuration error for missing required header filters" do
+ cache_cfg = {
+ 'metastore' => 'foo',
+ 'entitystore' => 'bar',
+ 'ignore_headers' => ['baz'] # `configure_cache` method is skipped to create a bad state
+ }
+
+ expect {
+ Frenetic::Configuration.new('url' => 'http://example.org', 'cache' => cache_cfg )
+ }.to raise_error(
+ Frenetic::Configuration::ConfigurationError, "Required cache header filters are missing!"
+ )
+ end
+ end
end
end
end

0 comments on commit 883e1f3

Please sign in to comment.