cached_resource 1.0.0
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 +5 -0
- data/.rspec +3 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README +36 -0
- data/Rakefile +8 -0
- data/cached_resource.gemspec +24 -0
- data/lib/cached_resource/caching.rb +72 -0
- data/lib/cached_resource/config.rb +26 -0
- data/lib/cached_resource/version.rb +3 -0
- data/lib/cached_resource.rb +43 -0
- data/spec/cached_resource/caching_spec.rb +104 -0
- data/spec/spec_helper.rb +17 -0
- metadata +133 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Andrew Chan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# CachedResource
|
2
|
+
CachedResource helps ActiveResource by caching responses according to request parameters. It can help reduce the lag created by making repeated requests across the network.
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
gem install cached_resource
|
6
|
+
|
7
|
+
## Configuration
|
8
|
+
CachedResource works "out of the box" with ActiveResource. By default, it caches responses to an `ActiveSupport::Cache::MemoryStore` and logs to an `ActiveSupport::BufferedLogger` attached to a `StringIO` object. *In a Rails 3 environment*, CachedResource will attach itself to the Rails logger and cache.
|
9
|
+
|
10
|
+
Turn CachedResource off. This will cause all ActiveResource responses to be retrieved normally (i.e. via the network).
|
11
|
+
|
12
|
+
CachedResource.off!
|
13
|
+
|
14
|
+
Turn CachedResource on.
|
15
|
+
|
16
|
+
CachedResource.on!
|
17
|
+
|
18
|
+
Set the cache expiry time to 60 seconds.
|
19
|
+
|
20
|
+
CachedResource.config.cache_time_to_live = 60
|
21
|
+
|
22
|
+
Set a different logger.
|
23
|
+
|
24
|
+
CachedResource.config.logger = MyLogger.new
|
25
|
+
|
26
|
+
Set a different cache store.
|
27
|
+
|
28
|
+
CachedResource.config.cache = MyCacheStore.new
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
Sit back and relax! If you need to reload a particular request you can do something like:
|
32
|
+
|
33
|
+
MyActiveResource.find(:all, :reload => true)
|
34
|
+
|
35
|
+
## Testing
|
36
|
+
rake
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "cached_resource/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "cached_resource"
|
7
|
+
s.version = CachedResource::VERSION
|
8
|
+
s.authors = "Andrew Chan"
|
9
|
+
s.email = "email@suspi.net"
|
10
|
+
s.homepage = "http://github.com/Ahsizara/cached_resource"
|
11
|
+
s.summary = %q{Caching for ActiveResource}
|
12
|
+
s.description = %q{Enables request-based caching for ActiveResource}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency "activeresource"
|
20
|
+
s.add_dependency "activesupport"
|
21
|
+
s.add_dependency "term-ansicolor"
|
22
|
+
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CachedResource
|
2
|
+
# The Caching module is included in ActiveResource and
|
3
|
+
# handles caching and recaching of responses.
|
4
|
+
module Caching
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# when included, setup a middle man for find
|
8
|
+
included do
|
9
|
+
class << self
|
10
|
+
alias_method_chain :find, :cache
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
# find a resource using the cache or resend the request
|
17
|
+
# if :reload is set to true or caching is disabled
|
18
|
+
def find_with_cache(*arguments)
|
19
|
+
arguments << {} unless arguments.last.is_a?(Hash)
|
20
|
+
should_reload = arguments.last.delete(:reload) || !CachedResource.config.cache_enabled
|
21
|
+
arguments.pop if arguments.last.empty?
|
22
|
+
key = cache_key(arguments)
|
23
|
+
|
24
|
+
begin
|
25
|
+
(should_reload ? find_via_reload(key, *arguments) : find_via_cache(key, *arguments))
|
26
|
+
rescue ActiveResource::ServerError, ActiveResource::ConnectionError, SocketError => e
|
27
|
+
raise(e)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# try to find a cached response for the given key. If
|
34
|
+
# no cache entry exists, send a new request.
|
35
|
+
def find_via_cache(key, *arguments)
|
36
|
+
result = CachedResource.cache.read(key).try(:dup)
|
37
|
+
result && log(:read, "#{key} for #{arguments.inspect}")
|
38
|
+
result || find_via_reload(key, *arguments)
|
39
|
+
end
|
40
|
+
|
41
|
+
# re/send the request to fetch the resource. Cache the response
|
42
|
+
# for the request.
|
43
|
+
def find_via_reload(key, *arguments)
|
44
|
+
result = find_without_cache(*arguments)
|
45
|
+
CachedResource.cache.write(key, result, :expires_in => CachedResource.config.cache_time_to_live)
|
46
|
+
log(:write, "#{key} for #{arguments.inspect}")
|
47
|
+
result
|
48
|
+
end
|
49
|
+
|
50
|
+
# generate the request cache key
|
51
|
+
def cache_key(*arguments)
|
52
|
+
"#{name.parameterize.gsub("-", "/")}/#{arguments.join('/')}".downcase
|
53
|
+
end
|
54
|
+
|
55
|
+
# log a message indicating a cached resource event
|
56
|
+
def log(type, msg)
|
57
|
+
c = Term::ANSIColor
|
58
|
+
type_string = "Cached Resource #{type.to_s.upcase}"
|
59
|
+
|
60
|
+
case type
|
61
|
+
when :read
|
62
|
+
type_string = c.intense_black + c.bold + type_string + c.clear
|
63
|
+
when :write
|
64
|
+
type_string = c.yellow + c.bold + type_string + c.clear
|
65
|
+
end
|
66
|
+
|
67
|
+
CachedResource.logger.info "#{type_string} #{msg}"
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module CachedResource
|
2
|
+
# The Config class is a singleton that contains
|
3
|
+
# global configuration options for CacheResource
|
4
|
+
class Config
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
# set default cache time to live to 1 week
|
8
|
+
DEFAULT_CACHE_TIME_TO_LIVE = 604800
|
9
|
+
|
10
|
+
attr_accessor :cache_enabled, :cache_time_to_live, :logger, :cache
|
11
|
+
|
12
|
+
# initialize the config with caching enabled and
|
13
|
+
# a default cache expiry of 7 days. Also initializes
|
14
|
+
# the logging and caching mechanisms, setting them to
|
15
|
+
# the Rails logger and cache if available. If unavailable,
|
16
|
+
# sets them to active support equivalents
|
17
|
+
def initialize
|
18
|
+
@cache_enabled = true
|
19
|
+
@cache_time_to_live = DEFAULT_CACHE_TIME_TO_LIVE
|
20
|
+
|
21
|
+
@cache = defined?(Rails.cache) && Rails.cache || ActiveSupport::Cache::MemoryStore.new
|
22
|
+
@logger = defined?(Rails.logger) && Rails.logger || ActiveSupport::BufferedLogger.new(StringIO.new)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# sourced from this great gist: https://gist.github.com/947734
|
2
|
+
require 'singleton'
|
3
|
+
require 'term/ansicolor'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
require 'active_support/concern'
|
7
|
+
require 'cached_resource/config'
|
8
|
+
require 'cached_resource/caching'
|
9
|
+
require 'cached_resource/version'
|
10
|
+
|
11
|
+
module CachedResource
|
12
|
+
|
13
|
+
# Switch cache usage off
|
14
|
+
def self.off!
|
15
|
+
self.config.cache_enabled = false
|
16
|
+
end
|
17
|
+
|
18
|
+
# Switch cache usage on
|
19
|
+
def self.on!
|
20
|
+
self.config.cache_enabled = true
|
21
|
+
end
|
22
|
+
|
23
|
+
# retrieve the configured logger
|
24
|
+
def self.logger
|
25
|
+
config.logger
|
26
|
+
end
|
27
|
+
|
28
|
+
# retrieve the configured cache store
|
29
|
+
def self.cache
|
30
|
+
config.cache
|
31
|
+
end
|
32
|
+
|
33
|
+
# Retrieve the configuration object
|
34
|
+
def self.config
|
35
|
+
@@config ||= CachedResource::Config.instance
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
# Include caching in ActiveResource::Base
|
41
|
+
class ActiveResource::Base
|
42
|
+
include CachedResource::Caching
|
43
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CachedResource do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
class Thing < ActiveResource::Base
|
7
|
+
self.site = "http://api.thing.com"
|
8
|
+
end
|
9
|
+
|
10
|
+
@thing = {:thing => {:id => 1, :name => "Ada"}}
|
11
|
+
@other_thing = {:thing => {:id => 1, :name => "Ari"}}
|
12
|
+
@thing_json = @thing.to_json
|
13
|
+
@other_thing_json = @other_thing.to_json
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "when enabled" do
|
17
|
+
|
18
|
+
before(:all) do
|
19
|
+
# it's on by default, but lets call the method
|
20
|
+
# to make sure it works
|
21
|
+
CachedResource.on!
|
22
|
+
|
23
|
+
ActiveResource::HttpMock.reset!
|
24
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
25
|
+
mock.get "/things/1.json", {}, @thing_json
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should cache a response" do
|
30
|
+
result = Thing.find(1)
|
31
|
+
CachedResource.config.cache.read("thing/1").should == result
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should read a response when the request is made again" do
|
35
|
+
Thing.find(1)
|
36
|
+
# only one request should have been made by the test
|
37
|
+
# before this one
|
38
|
+
ActiveResource::HttpMock.requests.length.should == 1
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should remake a request when reloaded" do
|
42
|
+
Thing.find(1, :reload => true)
|
43
|
+
ActiveResource::HttpMock.requests.length.should == 2
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should rewrite the cache when the request is reloaded" do
|
47
|
+
old_result = CachedResource.config.cache.read("thing/1")
|
48
|
+
|
49
|
+
# change the response
|
50
|
+
ActiveResource::HttpMock.reset!
|
51
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
52
|
+
mock.get "/things/1.json", {}, @other_thing_json
|
53
|
+
end
|
54
|
+
|
55
|
+
Thing.find(1, :reload => true)
|
56
|
+
new_result = CachedResource.config.cache.read("thing/1")
|
57
|
+
# since active resources are equal if and only if they
|
58
|
+
# are the same object or an instance of the same class,
|
59
|
+
# not new?, and have the same id.
|
60
|
+
new_result.name.should_not == old_result.name
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "when disabled" do
|
65
|
+
|
66
|
+
before(:all) do
|
67
|
+
CachedResource.off!
|
68
|
+
|
69
|
+
ActiveResource::HttpMock.reset!
|
70
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
71
|
+
mock.get "/things/1.json", {}, @thing_json
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should cache a response" do
|
76
|
+
result = Thing.find(1)
|
77
|
+
CachedResource.config.cache.read("thing/1").should == result
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should always remake the request" do
|
81
|
+
Thing.find(1)
|
82
|
+
ActiveResource::HttpMock.requests.length.should == 2
|
83
|
+
Thing.find(1)
|
84
|
+
ActiveResource::HttpMock.requests.length.should == 3
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should rewrite the cache for each request" do
|
88
|
+
old_result = CachedResource.config.cache.read("thing/1")
|
89
|
+
|
90
|
+
# change the response
|
91
|
+
ActiveResource::HttpMock.reset!
|
92
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
93
|
+
mock.get "/things/1.json", {}, @other_thing_json
|
94
|
+
end
|
95
|
+
|
96
|
+
Thing.find(1)
|
97
|
+
new_result = CachedResource.config.cache.read("thing/1")
|
98
|
+
# since active resources are equal if and only if they
|
99
|
+
# are the same object or an instance of the same class,
|
100
|
+
# not new?, and have the same id.
|
101
|
+
new_result.name.should_not == old_result.name
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'active_resource'
|
5
|
+
require 'active_support'
|
6
|
+
|
7
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
8
|
+
require 'cached_resource'
|
9
|
+
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
# nada
|
13
|
+
end
|
14
|
+
|
15
|
+
# clear cache at beginning and end of execution
|
16
|
+
CachedResource.cache.clear
|
17
|
+
at_exit { CachedResource.cache.clear }
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cached_resource
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Andrew Chan
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-10-20 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
type: :runtime
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 3
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
prerelease: false
|
32
|
+
name: activeresource
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
type: :runtime
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
hash: 3
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
prerelease: false
|
46
|
+
name: activesupport
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
type: :runtime
|
50
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
prerelease: false
|
60
|
+
name: term-ansicolor
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
type: :development
|
64
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
prerelease: false
|
74
|
+
name: rspec
|
75
|
+
version_requirements: *id004
|
76
|
+
description: Enables request-based caching for ActiveResource
|
77
|
+
email: email@suspi.net
|
78
|
+
executables: []
|
79
|
+
|
80
|
+
extensions: []
|
81
|
+
|
82
|
+
extra_rdoc_files: []
|
83
|
+
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- .rspec
|
87
|
+
- Gemfile
|
88
|
+
- MIT-LICENSE
|
89
|
+
- README
|
90
|
+
- Rakefile
|
91
|
+
- cached_resource.gemspec
|
92
|
+
- lib/cached_resource.rb
|
93
|
+
- lib/cached_resource/caching.rb
|
94
|
+
- lib/cached_resource/config.rb
|
95
|
+
- lib/cached_resource/version.rb
|
96
|
+
- spec/cached_resource/caching_spec.rb
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
homepage: http://github.com/Ahsizara/cached_resource
|
99
|
+
licenses: []
|
100
|
+
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
hash: 3
|
112
|
+
segments:
|
113
|
+
- 0
|
114
|
+
version: "0"
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
hash: 3
|
121
|
+
segments:
|
122
|
+
- 0
|
123
|
+
version: "0"
|
124
|
+
requirements: []
|
125
|
+
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 1.8.11
|
128
|
+
signing_key:
|
129
|
+
specification_version: 3
|
130
|
+
summary: Caching for ActiveResource
|
131
|
+
test_files:
|
132
|
+
- spec/cached_resource/caching_spec.rb
|
133
|
+
- spec/spec_helper.rb
|