cachext 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +48 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/cachext.gemspec +34 -0
- data/lib/cachext.rb +39 -0
- data/lib/cachext/client.rb +55 -0
- data/lib/cachext/configuration.rb +40 -0
- data/lib/cachext/features.rb +8 -0
- data/lib/cachext/features/backup.rb +20 -0
- data/lib/cachext/features/debug_logging.rb +50 -0
- data/lib/cachext/features/default.rb +14 -0
- data/lib/cachext/features/lock.rb +64 -0
- data/lib/cachext/key.rb +60 -0
- data/lib/cachext/options.rb +25 -0
- data/lib/cachext/version.rb +3 -0
- metadata +220 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3072a5999fa8a90d21259de13caec3f355eebdc4
|
4
|
+
data.tar.gz: 7ef116659a1bfa08174f3c27157752a3bdd13c8d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1c7308ee711bb3c43522f1801fe51f6de0653057a2e6cccd7e9ae0e27be8d42e7ce5285b3287a371f1fe5b26b6ffee1e789db1a1a793ec26fdc991a7ded85489
|
7
|
+
data.tar.gz: 3cb8a48dcd02fac5070bb695e35064fe5562f8303265c334e8846c44bf0a358e6b011e5a63ce60dfe1ea5d61e096f5f070652668ec2dea0fe929fd29d9cf84fe
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Donald Plummer
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Cachext
|
2
|
+
|
3
|
+
Extensions to normal Rails caching:
|
4
|
+
|
5
|
+
* Lock
|
6
|
+
* Backup
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'cachext'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
Replace `Rails.cache.fetch` with `Cachext.fetch`.
|
23
|
+
|
24
|
+
## Development
|
25
|
+
|
26
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
27
|
+
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
28
|
+
prompt that will allow you to experiment.
|
29
|
+
|
30
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
31
|
+
release a new version, update the version number in `version.rb`, and then run
|
32
|
+
`bundle exec rake release`, which will create a git tag for the version, push
|
33
|
+
git commits and tags, and push the `.gem` file to
|
34
|
+
[rubygems.org](https://rubygems.org).
|
35
|
+
|
36
|
+
Having trouble with a test? Set the `CACHEXT_DEBUG` environmental variable to
|
37
|
+
"true" to get debug logs.
|
38
|
+
|
39
|
+
## Contributing
|
40
|
+
|
41
|
+
Bug reports and pull requests are welcome on GitHub at
|
42
|
+
https://github.com/dplummer/cachext.
|
43
|
+
|
44
|
+
## License
|
45
|
+
|
46
|
+
The gem is available as open source under the terms of the
|
47
|
+
[MIT License](http://opensource.org/licenses/MIT).
|
48
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "cachext"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/cachext.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cachext/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cachext"
|
8
|
+
spec.version = Cachext::VERSION
|
9
|
+
spec.authors = ["Donald Plummer"]
|
10
|
+
spec.email = ["donald.plummer@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Cache with lock and backup extensions}
|
13
|
+
spec.description = %q{Don't calculate the cached value twice at the same time. Use a backup of the data if the service is down.}
|
14
|
+
spec.homepage = "https://github.com/dplummer/cachext"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "activesupport"
|
23
|
+
spec.add_dependency "redis"
|
24
|
+
spec.add_dependency "redis-namespace"
|
25
|
+
spec.add_dependency "redlock"
|
26
|
+
spec.add_dependency "faraday"
|
27
|
+
spec.add_dependency "dalli"
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
31
|
+
spec.add_development_dependency "rspec"
|
32
|
+
spec.add_development_dependency "pry"
|
33
|
+
spec.add_development_dependency "thread"
|
34
|
+
end
|
data/lib/cachext.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "cachext/version"
|
2
|
+
require "faraday/error"
|
3
|
+
|
4
|
+
module Cachext
|
5
|
+
autoload :Client, "cachext/client"
|
6
|
+
autoload :Configuration, "cachext/configuration"
|
7
|
+
autoload :Key, "cachext/key"
|
8
|
+
autoload :Features, "cachext/features"
|
9
|
+
autoload :Options, "cachext/options"
|
10
|
+
|
11
|
+
def self.Key raw_key
|
12
|
+
raw_key.is_a?(Key) ? raw_key : Key.new(raw_key)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.fetch raw_key, overrides = {}, &block
|
16
|
+
client.fetch Key(raw_key), overrides, &block
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.clear raw_key
|
20
|
+
Key(raw_key).clear
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.locked? raw_key
|
24
|
+
Key(raw_key).locked?
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.client
|
28
|
+
@client ||= Client.new config
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.config
|
32
|
+
@config ||= Configuration.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.flush
|
36
|
+
config.cache.clear
|
37
|
+
config.redis.del "cachext:*"
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "digest/sha1"
|
2
|
+
|
3
|
+
module Cachext
|
4
|
+
class Client
|
5
|
+
prepend Features::DebugLogging
|
6
|
+
prepend Features::Lock
|
7
|
+
prepend Features::Backup
|
8
|
+
prepend Features::Default
|
9
|
+
|
10
|
+
def initialize config
|
11
|
+
@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch key, options_hash, &block
|
15
|
+
options = Options.new @config, options_hash
|
16
|
+
|
17
|
+
retval = read key, options
|
18
|
+
return retval unless retval.nil?
|
19
|
+
|
20
|
+
call_block key, options, &block
|
21
|
+
|
22
|
+
rescue *Array(options.not_found_error) => e
|
23
|
+
handle_not_found key, options, e
|
24
|
+
rescue Features::Lock::TimeoutWaitingForLock, *options.errors => e
|
25
|
+
handle_error key, options, e
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def call_block key, options, &block
|
31
|
+
fresh = block.call
|
32
|
+
|
33
|
+
write key, fresh, options
|
34
|
+
|
35
|
+
fresh
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_not_found key, options, error
|
39
|
+
raise if options.reraise_errors
|
40
|
+
end
|
41
|
+
|
42
|
+
def handle_error key, options, error
|
43
|
+
@config.error_logger.error error
|
44
|
+
raise if @config.raise_errors && reraise_errors
|
45
|
+
end
|
46
|
+
|
47
|
+
def read key, options
|
48
|
+
key.read
|
49
|
+
end
|
50
|
+
|
51
|
+
def write key, fresh, options
|
52
|
+
key.write fresh, expires_in: options.expires_in
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "redlock"
|
2
|
+
require "redis-namespace"
|
3
|
+
|
4
|
+
module Cachext
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :raise_errors, # raise all errors all the time? (for tests)
|
7
|
+
:default_expires_in, # in seconds
|
8
|
+
:cache, # conform to ActiveSupport::Cache interface
|
9
|
+
:redis, # redis client for locking
|
10
|
+
:error_logger, # conform to Honeybadger interface
|
11
|
+
:default_errors, # array of errors to catch and not reraise
|
12
|
+
:not_found_errors, # array of errors where we delete the backup and reraise
|
13
|
+
:max_lock_wait, # time in seconds to wait for a lock
|
14
|
+
:debug, # output debug messages to STDERR
|
15
|
+
:heartbeat_expires # time in seconds for process heardbeat to expire
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
self.raise_errors = false
|
19
|
+
self.default_errors = [
|
20
|
+
Faraday::Error::ConnectionFailed,
|
21
|
+
Faraday::Error::TimeoutError,
|
22
|
+
]
|
23
|
+
self.not_found_errors = [
|
24
|
+
Faraday::Error::ResourceNotFound,
|
25
|
+
]
|
26
|
+
self.default_expires_in = 60
|
27
|
+
self.max_lock_wait = 5
|
28
|
+
self.debug = ENV['CACHEXT_DEBUG'] == "true"
|
29
|
+
self.heartbeat_expires = 2
|
30
|
+
end
|
31
|
+
|
32
|
+
def lock_manager
|
33
|
+
@lock_manager ||= Redlock::Client.new [lock_redis], retry_count: 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def lock_redis
|
37
|
+
@lock_redis ||= Redis::Namespace.new :cachext, redis: redis
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Cachext
|
2
|
+
module Features
|
3
|
+
module Backup
|
4
|
+
def handle_not_found key, options, error
|
5
|
+
key.delete_backup
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def handle_error key, options, error
|
10
|
+
super
|
11
|
+
key.read_backup
|
12
|
+
end
|
13
|
+
|
14
|
+
def write key, fresh, options
|
15
|
+
super
|
16
|
+
key.write_backup fresh
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Cachext
|
2
|
+
module Features
|
3
|
+
module DebugLogging
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def read key, options
|
8
|
+
retval = super
|
9
|
+
debug_log { { m: :read, key: key, retval: retval } }
|
10
|
+
retval
|
11
|
+
end
|
12
|
+
|
13
|
+
def handle_not_found key, options, error
|
14
|
+
debug_log { { m: :handle_not_found, key: key, error: error, reraise_errors: options.reraise_errors } }
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle_error key, options, error
|
19
|
+
debug_log { { m: :handle_error, key: key, error: error } }
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def obtain_lock key, options
|
24
|
+
lock_info = super
|
25
|
+
debug_log { { m: :obtain_lock, key: key }.merge(lock_info) }
|
26
|
+
lock_info
|
27
|
+
end
|
28
|
+
|
29
|
+
def wait_for_lock key, start_time
|
30
|
+
debug_log { { m: :wait_for_lock, key: key, waited: (Time.now - start_time) } }
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def write key, fresh, options
|
35
|
+
super
|
36
|
+
debug_log { { m: :write, key: key, fresh: fresh, expires_in: options.expires_in, read: key.read } }
|
37
|
+
end
|
38
|
+
|
39
|
+
def debug_log
|
40
|
+
if @config.debug
|
41
|
+
Thread.exclusive do
|
42
|
+
log = yield
|
43
|
+
msg = log.is_a?(String) ? log : log.inspect
|
44
|
+
$stderr.puts "[#{Time.now.to_s(:db)}] [#{Process.pid} #{Thread.current.object_id.to_s(16)}] #{msg}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Cachext
|
2
|
+
module Features
|
3
|
+
module Default
|
4
|
+
def handle_not_found key, options, error
|
5
|
+
super
|
6
|
+
options.default.respond_to?(:call) ? options.default.call(key) : options.default
|
7
|
+
end
|
8
|
+
|
9
|
+
def handle_error key, options, error
|
10
|
+
super || (options.default.respond_to?(:call) ? options.default.call(key) : options.default)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Cachext
|
2
|
+
module Features
|
3
|
+
module Lock
|
4
|
+
TimeoutWaitingForLock = Class.new(StandardError)
|
5
|
+
|
6
|
+
def read key, options
|
7
|
+
retval = super
|
8
|
+
return retval unless retval.nil?
|
9
|
+
|
10
|
+
@lock_info = obtain_lock key, options
|
11
|
+
|
12
|
+
retval = super
|
13
|
+
|
14
|
+
if !retval.nil?
|
15
|
+
@config.lock_manager.unlock @lock_info
|
16
|
+
end
|
17
|
+
|
18
|
+
retval
|
19
|
+
end
|
20
|
+
|
21
|
+
def call_block key, options, &block
|
22
|
+
with_heartbeat_extender key.digest, options.heartbeat_expires do
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def with_heartbeat_extender lock_key, heartbeat_expires, &block
|
28
|
+
done = false
|
29
|
+
heartbeat_frequency = heartbeat_expires / 2
|
30
|
+
|
31
|
+
Thread.new do
|
32
|
+
loop do
|
33
|
+
break if done
|
34
|
+
sleep heartbeat_frequency
|
35
|
+
break if done
|
36
|
+
@config.lock_manager.lock lock_key, (heartbeat_expires * 1000).ceil, extend: @lock_info
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
block.call
|
41
|
+
ensure
|
42
|
+
@config.lock_manager.unlock @lock_info
|
43
|
+
done = true
|
44
|
+
end
|
45
|
+
|
46
|
+
def obtain_lock key, options
|
47
|
+
start_time = Time.now
|
48
|
+
|
49
|
+
until lock_info = @config.lock_manager.lock(key.digest, (options.heartbeat_expires * 1000).ceil)
|
50
|
+
wait_for_lock key, start_time
|
51
|
+
end
|
52
|
+
|
53
|
+
lock_info
|
54
|
+
end
|
55
|
+
|
56
|
+
def wait_for_lock key, start_time
|
57
|
+
sleep rand
|
58
|
+
if Time.now - start_time > @config.max_lock_wait
|
59
|
+
raise TimeoutWaitingForLock
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/cachext/key.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Cachext
|
2
|
+
class Key
|
3
|
+
attr_reader :raw
|
4
|
+
|
5
|
+
def initialize(raw, config: Cachext.config)
|
6
|
+
@raw = Array(raw)
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
"#<Cachext::Key:#{object_id.to_s(16)} @raw=#{@raw.inspect} digest=#{digest}>"
|
12
|
+
end
|
13
|
+
|
14
|
+
def digest
|
15
|
+
::Digest::SHA1.hexdigest ::Marshal.dump(raw)
|
16
|
+
end
|
17
|
+
|
18
|
+
def backup
|
19
|
+
[:backup_cache] + raw
|
20
|
+
end
|
21
|
+
|
22
|
+
def locked?
|
23
|
+
lock_redis.exists digest
|
24
|
+
end
|
25
|
+
|
26
|
+
def read
|
27
|
+
cache.read raw
|
28
|
+
end
|
29
|
+
|
30
|
+
def write value, options = {}
|
31
|
+
cache.write raw, value, options
|
32
|
+
end
|
33
|
+
|
34
|
+
def clear
|
35
|
+
cache.delete raw
|
36
|
+
end
|
37
|
+
|
38
|
+
def read_backup
|
39
|
+
cache.read backup
|
40
|
+
end
|
41
|
+
|
42
|
+
def write_backup value
|
43
|
+
cache.write backup, value
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete_backup
|
47
|
+
cache.delete backup
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def cache
|
53
|
+
@config.cache
|
54
|
+
end
|
55
|
+
|
56
|
+
def lock_redis
|
57
|
+
@config.lock_redis
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
module Cachext
|
3
|
+
class Options
|
4
|
+
attr_reader :expires_in,
|
5
|
+
:default,
|
6
|
+
:errors,
|
7
|
+
:reraise_errors,
|
8
|
+
:not_found_error,
|
9
|
+
:heartbeat_expires
|
10
|
+
|
11
|
+
def initialize config, expires_in: config.default_expires_in,
|
12
|
+
default: nil,
|
13
|
+
errors: config.default_errors,
|
14
|
+
reraise_errors: true,
|
15
|
+
not_found_error: config.not_found_errors,
|
16
|
+
heartbeat_expires: config.heartbeat_expires
|
17
|
+
@expires_in = expires_in
|
18
|
+
@default = default
|
19
|
+
@errors = errors
|
20
|
+
@reraise_errors = reraise_errors
|
21
|
+
@not_found_error = not_found_error
|
22
|
+
@heartbeat_expires = heartbeat_expires
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cachext
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Donald Plummer
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: redis
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: redis-namespace
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: redlock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: faraday
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: dalli
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: bundler
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.10'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.10'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '10.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '10.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: thread
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
description: Don't calculate the cached value twice at the same time. Use a backup
|
168
|
+
of the data if the service is down.
|
169
|
+
email:
|
170
|
+
- donald.plummer@gmail.com
|
171
|
+
executables: []
|
172
|
+
extensions: []
|
173
|
+
extra_rdoc_files: []
|
174
|
+
files:
|
175
|
+
- ".gitignore"
|
176
|
+
- ".rspec"
|
177
|
+
- ".travis.yml"
|
178
|
+
- Gemfile
|
179
|
+
- LICENSE.txt
|
180
|
+
- README.md
|
181
|
+
- Rakefile
|
182
|
+
- bin/console
|
183
|
+
- bin/setup
|
184
|
+
- cachext.gemspec
|
185
|
+
- lib/cachext.rb
|
186
|
+
- lib/cachext/client.rb
|
187
|
+
- lib/cachext/configuration.rb
|
188
|
+
- lib/cachext/features.rb
|
189
|
+
- lib/cachext/features/backup.rb
|
190
|
+
- lib/cachext/features/debug_logging.rb
|
191
|
+
- lib/cachext/features/default.rb
|
192
|
+
- lib/cachext/features/lock.rb
|
193
|
+
- lib/cachext/key.rb
|
194
|
+
- lib/cachext/options.rb
|
195
|
+
- lib/cachext/version.rb
|
196
|
+
homepage: https://github.com/dplummer/cachext
|
197
|
+
licenses:
|
198
|
+
- MIT
|
199
|
+
metadata: {}
|
200
|
+
post_install_message:
|
201
|
+
rdoc_options: []
|
202
|
+
require_paths:
|
203
|
+
- lib
|
204
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
209
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
210
|
+
requirements:
|
211
|
+
- - ">="
|
212
|
+
- !ruby/object:Gem::Version
|
213
|
+
version: '0'
|
214
|
+
requirements: []
|
215
|
+
rubyforge_project:
|
216
|
+
rubygems_version: 2.4.5.1
|
217
|
+
signing_key:
|
218
|
+
specification_version: 4
|
219
|
+
summary: Cache with lock and backup extensions
|
220
|
+
test_files: []
|