api_cache 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/{README.rdoc → README.md} +27 -8
- data/Rakefile +10 -0
- data/TODO +0 -0
- data/api_cache.gemspec +25 -0
- data/init.rb +2 -0
- data/lib/api_cache.rb +2 -0
- data/lib/api_cache/dalli_store.rb +29 -0
- data/lib/api_cache/null_store.rb +20 -0
- data/spec/api_cache_spec.rb +1 -1
- data/spec/api_spec.rb +1 -1
- data/spec/cache_spec.rb +1 -1
- data/spec/dalli_store_spec.rb +30 -0
- data/spec/integration_spec.rb +1 -1
- data/spec/monteta_store_spec.rb +2 -1
- data/spec/null_store_spec.rb +19 -0
- data/spec/spec_helper.rb +3 -3
- metadata +85 -21
- data/VERSION.yml +0 -4
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008-2011 Martyn Loughran
|
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.rdoc → README.md}
RENAMED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
# APICache (aka api_cache)
|
2
2
|
|
3
3
|
APICache allows any API client library to be easily wrapped with a robust caching layer. It supports caching (obviously), serving stale data and limits on the number of API calls. It's also got a handy syntax if all you want to do is cache a bothersome url.
|
4
4
|
|
5
|
-
|
5
|
+
## For the impatient
|
6
6
|
|
7
7
|
# Install
|
8
8
|
sudo gem install api_cache -s http://gemcutter.org
|
@@ -15,6 +15,7 @@ APICache allows any API client library to be easily wrapped with a robust cachin
|
|
15
15
|
APICache.get("http://twitter.com/statuses/public_timeline.rss")
|
16
16
|
|
17
17
|
# Use a proper store
|
18
|
+
require 'moneta'
|
18
19
|
require 'moneta/memcache'
|
19
20
|
APICache.store = Moneta::Memcache.new(:server => "localhost")
|
20
21
|
|
@@ -24,7 +25,16 @@ APICache allows any API client library to be easily wrapped with a robust cachin
|
|
24
25
|
FlickrRb.get_all_sets
|
25
26
|
end
|
26
27
|
|
27
|
-
|
28
|
+
## Heroku + memcache
|
29
|
+
|
30
|
+
Heroku memcache add-on users can use APICache with the [Dalli](https://github.com/mperham/dalli) gem, no manual configuration is required as Dalli automatically picks up the appropriate environment variables.
|
31
|
+
|
32
|
+
require 'api_cache'
|
33
|
+
require 'dalli'
|
34
|
+
|
35
|
+
APICache.store = APICache::DalliStore.new(Dalli::Client.new)
|
36
|
+
|
37
|
+
## The longer version
|
28
38
|
|
29
39
|
You want to use the Twitter API but you don't want to die?
|
30
40
|
|
@@ -36,7 +46,7 @@ This works better than a standard HTTP get because you get the following functio
|
|
36
46
|
* Stale response returned for a day if twitter is down
|
37
47
|
* Limited to attempt a connection at most once a minute
|
38
48
|
|
39
|
-
To understand what
|
49
|
+
To understand what `APICache` does here's an example: Given cached data less than 10 minutes old, it returns that. Otherwise, assuming it didn't try to request the URL within the last minute (to avoid the rate limit), it makes a get request to the supplied url. If the Twitter API timeouts or doesn't return a 2xx code (very likely) we're still fine: it just returns the last data fetched (as long as it's less than a day old). In the exceptional case that all is lost and no data can be returned, a subclass of `APICache::APICacheError` is raised which you're responsible for rescuing.
|
40
50
|
|
41
51
|
Assuming that you don't care whether it was a timeout error or an invalid response (for example) you could do this:
|
42
52
|
|
@@ -69,9 +79,9 @@ The real value however is not caching HTTP calls, but allowing caching functiona
|
|
69
79
|
end
|
70
80
|
end
|
71
81
|
|
72
|
-
The first argument to
|
82
|
+
The first argument to `APICache.get` is now assumed to be a unique key rather than a URL. As you'd expect, the block will only be called if the request cannot be fulfilled by the cache. Throwing any exception signals to `APICache` that the request was not successful, should not be cached, and a cached value should be returned if available. If a cached value is not available then the exception will be re-raised for you to handle.
|
73
83
|
|
74
|
-
You can send any of the following options to
|
84
|
+
You can send any of the following options to `APICache.get(url, options = {}, &block)`. These are the default values (times are all in seconds):
|
75
85
|
|
76
86
|
{
|
77
87
|
:cache => 600, # 10 minutes After this time fetch new data
|
@@ -89,6 +99,15 @@ Before using the APICache you should set the cache to use. By default an in memo
|
|
89
99
|
|
90
100
|
Please be liberal with the github issue tracker, more so with pull requests, or drop me a mail to me [at] mloughran [dot] com. I'd love to hear from you.
|
91
101
|
|
92
|
-
|
102
|
+
## Contributing
|
103
|
+
|
104
|
+
There is a Gemfile to make running the specs easy:
|
105
|
+
|
106
|
+
bundle install
|
107
|
+
bundle exec rake
|
108
|
+
|
109
|
+
Code, write specs, send pull request, easy as pie. Thanks!
|
110
|
+
|
111
|
+
## Copyright
|
93
112
|
|
94
|
-
Copyright (c) 2008 Martyn Loughran. See LICENSE for details.
|
113
|
+
Copyright (c) 2008-2011 Martyn Loughran. See LICENSE for details.
|
data/Rakefile
ADDED
data/TODO
ADDED
File without changes
|
data/api_cache.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "api_cache"
|
6
|
+
s.version = "0.2.1"
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["Martyn Loughran"]
|
9
|
+
s.email = ["me@mloughran.com"]
|
10
|
+
s.homepage = "http://mloughran.github.com/api_cache/"
|
11
|
+
s.summary = %q{API Cache allows advanced caching of APIs}
|
12
|
+
s.description = %q{APICache allows any API client library to be easily wrapped with a robust caching layer. It supports caching (obviously), serving stale data and limits on the number of API calls. It's also got a handy syntax if all you want to do is cache a bothersome url.}
|
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_development_dependency('rspec', "~> 1.0")
|
20
|
+
s.add_development_dependency('fakeweb')
|
21
|
+
s.add_development_dependency('rake')
|
22
|
+
s.add_development_dependency('moneta', "~> 0.6.0")
|
23
|
+
s.add_development_dependency('dalli')
|
24
|
+
s.add_development_dependency('memcache-client')
|
25
|
+
end
|
data/init.rb
ADDED
data/lib/api_cache.rb
CHANGED
@@ -133,5 +133,7 @@ require 'api_cache/cache'
|
|
133
133
|
require 'api_cache/api'
|
134
134
|
|
135
135
|
APICache.autoload 'AbstractStore', 'api_cache/abstract_store'
|
136
|
+
APICache.autoload 'DalliStore', 'api_cache/dalli_store'
|
136
137
|
APICache.autoload 'MemoryStore', 'api_cache/memory_store'
|
137
138
|
APICache.autoload 'MonetaStore', 'api_cache/moneta_store'
|
139
|
+
APICache.autoload 'NullStore', 'api_cache/null_store'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class APICache
|
2
|
+
class DalliStore < APICache::AbstractStore
|
3
|
+
def initialize(store)
|
4
|
+
@dalli = store
|
5
|
+
end
|
6
|
+
|
7
|
+
# Set value. Returns true if success.
|
8
|
+
def set(key, value)
|
9
|
+
@dalli.set(key, value)
|
10
|
+
@dalli.set("#{key}_created_at", Time.now)
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get value.
|
15
|
+
def get(key)
|
16
|
+
@dalli.get(key)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Does a given key exist in the cache?
|
20
|
+
def exists?(key)
|
21
|
+
!get(key).nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
# Has a given time passed since the key was set?
|
25
|
+
def expired?(key, timeout)
|
26
|
+
Time.now - @dalli.get("#{key}_created_at") > timeout
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class APICache
|
2
|
+
# A null store for environments where caching may be undesirable, such as
|
3
|
+
# testing.
|
4
|
+
class NullStore < APICache::AbstractStore
|
5
|
+
def initialize
|
6
|
+
end
|
7
|
+
|
8
|
+
def exists?(key)
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
def set(key, value)
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def expired?(key, timeout)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/spec/api_cache_spec.rb
CHANGED
data/spec/api_spec.rb
CHANGED
data/spec/cache_spec.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'dalli'
|
4
|
+
|
5
|
+
describe APICache::DalliStore do
|
6
|
+
before :each do
|
7
|
+
@dalli = Dalli::Client.new('localhost:11211')
|
8
|
+
@dalli.delete('foo')
|
9
|
+
@store = APICache::DalliStore.new(@dalli)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should set and get' do
|
13
|
+
@store.set('key', 'value')
|
14
|
+
@store.get('key').should == 'value'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should allow checking whether a key exists' do
|
18
|
+
@store.exists?('foo').should be_false
|
19
|
+
@store.set('foo', 'bar')
|
20
|
+
@store.exists?('foo').should be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should allow checking whether a given amount of time has passed since the key was set' do
|
24
|
+
@store.expired?('foo', 1).should be_false
|
25
|
+
@store.set('foo', 'bar')
|
26
|
+
@store.expired?('foo', 1).should be_false
|
27
|
+
sleep 1
|
28
|
+
@store.expired?('foo', 1).should be_true
|
29
|
+
end
|
30
|
+
end
|
data/spec/integration_spec.rb
CHANGED
data/spec/monteta_store_spec.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe APICache::NullStore do
|
4
|
+
before :each do
|
5
|
+
@store = APICache::NullStore.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should NOT set" do
|
9
|
+
@store.exists?('foo').should be_false
|
10
|
+
@store.set('foo', 'bar')
|
11
|
+
@store.exists?('foo').should be_false
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should allows say keys are expired" do
|
15
|
+
@store.expired?('foo', 1).should be_true
|
16
|
+
@store.set('foo', 'bar')
|
17
|
+
@store.expired?('foo', 1).should be_true
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease:
|
5
|
+
version: 0.2.1
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Martyn Loughran
|
@@ -9,31 +10,78 @@ autorequire:
|
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date:
|
13
|
+
date: 2011-03-25 00:00:00 +00:00
|
13
14
|
default_executable:
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
16
17
|
name: rspec
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "1.0"
|
17
25
|
type: :development
|
18
|
-
|
19
|
-
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: fakeweb
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
20
32
|
requirements:
|
21
33
|
- - ">="
|
22
34
|
- !ruby/object:Gem::Version
|
23
35
|
version: "0"
|
24
|
-
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
25
38
|
- !ruby/object:Gem::Dependency
|
26
|
-
name:
|
39
|
+
name: rake
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: moneta
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ~>
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 0.6.0
|
58
|
+
type: :development
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: dalli
|
62
|
+
prerelease: false
|
63
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
27
69
|
type: :development
|
28
|
-
|
29
|
-
|
70
|
+
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: memcache-client
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
30
76
|
requirements:
|
31
77
|
- - ">="
|
32
78
|
- !ruby/object:Gem::Version
|
33
79
|
version: "0"
|
34
|
-
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id006
|
35
82
|
description: APICache allows any API client library to be easily wrapped with a robust caching layer. It supports caching (obviously), serving stale data and limits on the number of API calls. It's also got a handy syntax if all you want to do is cache a bothersome url.
|
36
|
-
email:
|
83
|
+
email:
|
84
|
+
- me@mloughran.com
|
37
85
|
executables: []
|
38
86
|
|
39
87
|
extensions: []
|
@@ -41,48 +89,64 @@ extensions: []
|
|
41
89
|
extra_rdoc_files: []
|
42
90
|
|
43
91
|
files:
|
44
|
-
-
|
45
|
-
-
|
92
|
+
- .gitignore
|
93
|
+
- Gemfile
|
94
|
+
- LICENSE
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- TODO
|
98
|
+
- api_cache.gemspec
|
99
|
+
- init.rb
|
100
|
+
- lib/api_cache.rb
|
46
101
|
- lib/api_cache/abstract_store.rb
|
47
102
|
- lib/api_cache/api.rb
|
48
103
|
- lib/api_cache/cache.rb
|
104
|
+
- lib/api_cache/dalli_store.rb
|
49
105
|
- lib/api_cache/memory_store.rb
|
50
106
|
- lib/api_cache/moneta_store.rb
|
51
|
-
- lib/api_cache.rb
|
107
|
+
- lib/api_cache/null_store.rb
|
52
108
|
- spec/api_cache_spec.rb
|
53
109
|
- spec/api_spec.rb
|
54
110
|
- spec/cache_spec.rb
|
111
|
+
- spec/dalli_store_spec.rb
|
55
112
|
- spec/integration_spec.rb
|
56
113
|
- spec/monteta_store_spec.rb
|
114
|
+
- spec/null_store_spec.rb
|
57
115
|
- spec/spec_helper.rb
|
58
116
|
has_rdoc: true
|
59
117
|
homepage: http://mloughran.github.com/api_cache/
|
60
118
|
licenses: []
|
61
119
|
|
62
120
|
post_install_message:
|
63
|
-
rdoc_options:
|
64
|
-
|
65
|
-
- --charset=UTF-8
|
121
|
+
rdoc_options: []
|
122
|
+
|
66
123
|
require_paths:
|
67
124
|
- lib
|
68
125
|
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
69
127
|
requirements:
|
70
128
|
- - ">="
|
71
129
|
- !ruby/object:Gem::Version
|
72
130
|
version: "0"
|
73
|
-
version:
|
74
131
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
75
133
|
requirements:
|
76
134
|
- - ">="
|
77
135
|
- !ruby/object:Gem::Version
|
78
136
|
version: "0"
|
79
|
-
version:
|
80
137
|
requirements: []
|
81
138
|
|
82
139
|
rubyforge_project:
|
83
|
-
rubygems_version: 1.3
|
140
|
+
rubygems_version: 1.5.3
|
84
141
|
signing_key:
|
85
142
|
specification_version: 3
|
86
143
|
summary: API Cache allows advanced caching of APIs
|
87
|
-
test_files:
|
88
|
-
|
144
|
+
test_files:
|
145
|
+
- spec/api_cache_spec.rb
|
146
|
+
- spec/api_spec.rb
|
147
|
+
- spec/cache_spec.rb
|
148
|
+
- spec/dalli_store_spec.rb
|
149
|
+
- spec/integration_spec.rb
|
150
|
+
- spec/monteta_store_spec.rb
|
151
|
+
- spec/null_store_spec.rb
|
152
|
+
- spec/spec_helper.rb
|
data/VERSION.yml
DELETED