CacheAndFetch
A simple gem that allows to add a soft expiry to your cache. If your cache soft expires, you get the stale record to work with, while it fetches the fresh record from the source in background.
Installation
Add this line to your application's Gemfile:
gem 'cache_and_fetch'
And then execute:
$ bundle
Or install it yourself as:
$ gem install cache_and_fetch
Usage
class Test < ActiveResource::Base
self.site = 'http://test.example.com'
include CacheAndFetch::Fetchable
end
You can set a custom cache expiration limit for the class. If this is not set, the soft cache expires after 20 minutes by default.
self.cache_expiration = 10.minutes
When invoked for the first time(cold cache), the gem goes and fetches the resource from a remote location.
Test.fetch(1) # #<Test:0x007f8e33daf980 @attributes={"id"=>1, "name"=>"test"}, @prefix_options={}, @persisted=true, @cache_expires_at=1370049015>
After sometime, let's say the resource has updated it's name to 'new test'. However, the soft cache has not yet expired. When we try to fetch the resource again, it returns the cached value.
Test.fetch(1) # #<Test:0x007f8e33daf980 @attributes={"id"=>1, "name"=>"test"}, @prefix_options={}, @persisted=true, @cache_expires_at=1370049015>
After 10 mins, when we try to fetch the record again, it returns the value from the cache. But spins off an async process, to fetch the fresh resource and update the cache.
Test.fetch(1) # #<Test:0x007f8e33daf980 @attributes={"id"=>1, "name"=>"test"}, @prefix_options={}, @persisted=true, @cache_expires_at=1370049015>
When we try to fetch the record again next time, it gives us the new data which is now in the cache.
Test.fetch(1) # #<Test:0x007f8e33daf980 @attributes={"id"=>1, "name"=>"new test"}, @prefix_options={}, @persisted=true, @cache_expires_at=1370049915>
If the default async fetching is not prefered, you can pass a custom block to fetch the fresh data.
Test.fetch(1) do |resource|
resource.delay(:queue => 'caching').recache # This example is using delayed_jobs
end
The gem however works for any class that responds to a class level 'find' method. However, it allows one the flexibility to define a custom Finder module. The gem automatically extends the class with the custom finder module.
In the following example, notice how the class does not inherit ActiveResource::Base.
class SimpleTest
include CacheAndFetch::Fetchable
attr_accessor :id
def initialize(id)
@id = id
end
end
A finder module needs to be defined with it's name as <class_name>Finder. The class SimpleTest will get extended by this module. The logic of find can either call a remote web service or even fetch it from a database.
module SimpleTest::Finder
def find(id)
response = HTTParty.get("http://example.com/simple_tests/#{id}.json") # This example uses httparty
self.new(response.body)
end
end
The gem allows you to set the method name you want to use as primary key. Of course, your instance must respond to the method you have set to extract the primary key.
class AnotherTest
include CacheAndFetch::Fetchable
attr_accessor :guid
self.primary_key = :guid
def initialize(guid)
@guid = guid
end
end
So, now when you do a Test.fetch(1)
, the argument for fetch
represents the primary key of the object. In this case, it is the guid
of the object.
The gem makes use of Rails caching mechanism. Hence, it only works with Rails as of now.
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
- Or update documentation
License
Copyright (c) 2013 Suman Mukherjee
MIT License
For more information on license, please look at LICENSE.txt