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.
@@ -0,0 +1,4 @@
1
+ pkg
2
+ .DS_Store
3
+ rdoc
4
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in api_cache.gemspec
4
+ gemspec
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.
@@ -1,8 +1,8 @@
1
- = APICache (aka api_cache)
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
- == For the impatient
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
- == The longer version
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 <tt>APICache</tt> 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 <tt>APICache::APICacheError</tt> is raised which you're responsible for rescuing.
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 <tt>APICache.get</tt> 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 <tt>APICache</tt> 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.
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 <tt>APICache.get(url, options = {}, &block)</tt>. These are the default values (times are all in seconds):
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
- == Copyright
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.
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'spec/rake/spectask'
5
+ Spec::Rake::SpecTask.new(:spec) do |t|
6
+ t.libs << 'lib' << 'spec'
7
+ t.spec_files = FileList['spec/**/*_spec.rb']
8
+ end
9
+
10
+ task :default => :spec
data/TODO ADDED
File without changes
@@ -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
@@ -0,0 +1,2 @@
1
+ # To use api_cache as a rails plugin
2
+ require "api_cache"
@@ -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
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe APICache do
4
4
  before :each do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe APICache::API do
4
4
  before :each do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe APICache::Cache do
4
4
  before :each do
@@ -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
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe "api_cache" do
4
4
  before :each do
@@ -1,5 +1,6 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
+ require 'moneta'
3
4
  require 'moneta/memcache'
4
5
 
5
6
  describe APICache::MonetaStore do
@@ -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
@@ -1,7 +1,7 @@
1
- $:.push File.join(File.dirname(__FILE__), '..', 'lib')
2
- require "rubygems"
3
- require "api_cache"
4
1
  require "spec"
5
2
  require "fakeweb"
6
3
 
4
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
5
+ require "api_cache"
6
+
7
7
  APICache.logger.level = Logger::FATAL
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
- version: 0.2.0
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: 2009-08-21 00:00:00 +02:00
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
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
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
- version:
36
+ type: :development
37
+ version_requirements: *id002
25
38
  - !ruby/object:Gem::Dependency
26
- name: fakeweb
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
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
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
- version:
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: me@mloughran.com
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
- - README.rdoc
45
- - VERSION.yml
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
- - --inline-source
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.5
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
@@ -1,4 +0,0 @@
1
- ---
2
- :patch: 0
3
- :major: 0
4
- :minor: 2