redis-expiring-set 0.1.0
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/README.markdown +92 -0
- data/Rakefile +9 -0
- data/lib/redis-expiring-set.rb +46 -0
- data/lib/redis-expiring-set/monkeypatch.rb +3 -0
- data/lib/redis-expiring-set/version.rb +5 -0
- data/redis-expiring-set.gemspec +25 -0
- data/test/partial_minispec.rb +229 -0
- data/test/test_helper.rb +12 -0
- data/test/unit/test_monkeypatched.rb +43 -0
- data/test/unit/test_namespaced.rb +43 -0
- metadata +73 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# Redis::ExpiringSet
|
2
|
+
|
3
|
+
## Purpose
|
4
|
+
|
5
|
+
Redis::ExpiringSet provides a very specific but useful data-type to your Redis
|
6
|
+
environment - a set of objects where each has its own specified expiration time.
|
7
|
+
|
8
|
+
Imagine, for example, that you want to keep details on every email that your
|
9
|
+
system sent out in the last 24 hours. Dump the data into an ExpiringSet with a
|
10
|
+
24-hour expiration time and let the set manage itself - you'll only ever see
|
11
|
+
data that is no more than 24 hours old.
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
The commands for Redis::ExpiringSet are designed to closely mirror those Redis
|
16
|
+
already has in place for working with sets. As of this release the commands are
|
17
|
+
not a complete copy of what's available for working with sets in Redis, but they
|
18
|
+
serve to be functional. Example usage:
|
19
|
+
|
20
|
+
require 'redis-expiring-set'
|
21
|
+
|
22
|
+
redis = Redis.new
|
23
|
+
expset = Redis::ExpiringSet.new(redis)
|
24
|
+
|
25
|
+
# add "something" to the set "some_set" that expires in 10 seconds
|
26
|
+
expset.xadd "some_set", "something", Time.now + 10
|
27
|
+
# This does the same thing; pass in a number instead of a Time object and it
|
28
|
+
# assumes you mean "expire x seconds from now"
|
29
|
+
expset.xadd "some_set", "something", 10
|
30
|
+
|
31
|
+
# Decide you want to remove the item before it expires? OK:
|
32
|
+
expset.xrem "some_set", "something"
|
33
|
+
|
34
|
+
# Need to know how big the set is?
|
35
|
+
expset.xcard "some_set"
|
36
|
+
|
37
|
+
# Want to actually look at your data?
|
38
|
+
expset.xmembers "some_set"
|
39
|
+
|
40
|
+
# By default, expired items are removed any time you go to read values
|
41
|
+
# (currently, when you call xcard and xmembers). If you want to clear them
|
42
|
+
# manually, though, you can use:
|
43
|
+
expset.xclearexpired "some_set"
|
44
|
+
|
45
|
+
## Alternate Usage
|
46
|
+
|
47
|
+
"Ugh, you mean I have to instantiate some new object and use that to interact
|
48
|
+
with this new data type?"
|
49
|
+
|
50
|
+
Not if you don't want to. There's an optional file you can require which
|
51
|
+
monkeypatches these methods onto the Redis class so that you can use it without
|
52
|
+
ever having to touch an instance of Redis::ExpiringSet. Example:
|
53
|
+
|
54
|
+
require 'redis-expiring-set/monkeypatch'
|
55
|
+
|
56
|
+
At this point you can use `xadd`, `xrem`, `xcard`, `xmembers`, and
|
57
|
+
`xclearexpired` just like they were native redis methods. This just isn't the
|
58
|
+
default functionality because I don't want you to have to monkeypatch Redis for
|
59
|
+
this if you don't want to.
|
60
|
+
|
61
|
+
## Performance
|
62
|
+
|
63
|
+
Redis::ExpiredSet uses nothing but native Redis functions to manipulate data,
|
64
|
+
and under the hood we use sorted sets. Expiring items in the cache is only done
|
65
|
+
when it's absolutely necessary, so there should be a minimal number of
|
66
|
+
unnecessary operations.
|
67
|
+
|
68
|
+
BUT, obviously, one of the chief advantages of using Redis is the performance,
|
69
|
+
so if you notice anything that might make this library any more performant, I'd
|
70
|
+
be glad to hear about it.
|
71
|
+
|
72
|
+
## Contributions
|
73
|
+
|
74
|
+
If Redis::ExpiringSet interests you and you think you might want to contribute, hit me up
|
75
|
+
over Github. You can also just fork it and make some changes, but there's a
|
76
|
+
better chance that your work won't be duplicated or rendered obsolete if you
|
77
|
+
check in on the current development status first.
|
78
|
+
|
79
|
+
## Development notes
|
80
|
+
|
81
|
+
You need Redis installed to do development on Redis::ExpiringSet, and currently the test
|
82
|
+
suites assume that you're using the default configurations for Redis. That
|
83
|
+
should probably change, but it probably won't until someone needs it to.
|
84
|
+
|
85
|
+
Gem requirements/etc. should be handled by Bundler.
|
86
|
+
|
87
|
+
## License
|
88
|
+
Copyright (C) 2012 by Tommy Morgan
|
89
|
+
|
90
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
91
|
+
|
92
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
require "redis"
|
4
|
+
require "redis-expiring-set/version"
|
5
|
+
|
6
|
+
|
7
|
+
class Redis
|
8
|
+
module ExpiringSetMethods
|
9
|
+
def xadd(key, value, expires)
|
10
|
+
if expires.is_a? Numeric
|
11
|
+
expires = Time.now + expires
|
12
|
+
end
|
13
|
+
|
14
|
+
zadd(key, expires.to_f, value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def xrem(key, value)
|
18
|
+
zrem(key, value)
|
19
|
+
end
|
20
|
+
|
21
|
+
def xmembers(key)
|
22
|
+
xclearexpired(key)
|
23
|
+
zrangebyscore(key, -Float::INFINITY, Float::INFINITY)
|
24
|
+
end
|
25
|
+
|
26
|
+
def xclearexpired(key)
|
27
|
+
zremrangebyscore key, -Float::INFINITY, Time.now.to_f.to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
def xcard(key)
|
31
|
+
xclearexpired(key)
|
32
|
+
zcard(key)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ExpiringSet
|
37
|
+
extend Forwardable
|
38
|
+
include ExpiringSetMethods
|
39
|
+
|
40
|
+
def_delegators :@redis, :zadd, :zcard, :zrem, :zrangebyscore, :zremrangebyscore
|
41
|
+
|
42
|
+
def initialize(redis)
|
43
|
+
@redis = redis
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "redis-expiring-set/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "redis-expiring-set"
|
7
|
+
s.version = Redis::ExpiringSet::VERSION
|
8
|
+
s.authors = ["Tommy Morgan"]
|
9
|
+
s.email = ["tommy.morgan@gmail.com"]
|
10
|
+
s.homepage = "http://github.com/duwanis/redis-expiring-set"
|
11
|
+
s.summary = %q{Add an expiring set to redis.}
|
12
|
+
s.description = %q{Add some commands to Redis that make it easy to work with a set of objects that need to expire individually.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "redis-expiring-set"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
s.add_runtime_dependency "redis"
|
25
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
## This is code stolen from the definition file for Minitest::Spec. We really
|
2
|
+
# like everything about Minitest::Spec except for the expectations part, so we
|
3
|
+
# are stealing it and using it here. Shamelessly.
|
4
|
+
#
|
5
|
+
# This code may go out of date in future versions of Ruby, so we should keep an
|
6
|
+
# eye on that. But that's better than reinventing the wheel.
|
7
|
+
|
8
|
+
module Kernel # :nodoc:
|
9
|
+
##
|
10
|
+
# Describe a series of expectations for a given target +desc+.
|
11
|
+
#
|
12
|
+
# TODO: find good tutorial url.
|
13
|
+
#
|
14
|
+
# Defines a test class subclassing from either MiniTest::Spec or
|
15
|
+
# from the surrounding describe's class. The surrounding class may
|
16
|
+
# subclass MiniTest::Spec manually in order to easily share code:
|
17
|
+
#
|
18
|
+
# class MySpec < MiniTest::Spec
|
19
|
+
# # ... shared code ...
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# class TestStuff < MySpec
|
23
|
+
# it "does stuff" do
|
24
|
+
# # shared code available here
|
25
|
+
# end
|
26
|
+
# describe "inner stuff" do
|
27
|
+
# it "still does stuff" do
|
28
|
+
# # ...and here
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
|
33
|
+
def describe desc, additional_desc = nil, &block # :doc:
|
34
|
+
stack = MiniTest::Spec.describe_stack
|
35
|
+
name = [stack.last, desc, additional_desc].compact.join("::")
|
36
|
+
sclas = stack.last || if Class === self && self < MiniTest::Spec then
|
37
|
+
self
|
38
|
+
else
|
39
|
+
MiniTest::Spec.spec_type desc
|
40
|
+
end
|
41
|
+
|
42
|
+
cls = sclas.create name, desc
|
43
|
+
|
44
|
+
stack.push cls
|
45
|
+
cls.class_eval(&block)
|
46
|
+
stack.pop
|
47
|
+
cls
|
48
|
+
end
|
49
|
+
private :describe
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# MiniTest::Spec -- The faster, better, less-magical spec framework!
|
54
|
+
#
|
55
|
+
# For a list of expectations, see MiniTest::Expectations.
|
56
|
+
|
57
|
+
class MiniTest::Spec < MiniTest::Unit::TestCase
|
58
|
+
##
|
59
|
+
# Contains pairs of matchers and Spec classes to be used to
|
60
|
+
# calculate the superclass of a top-level describe. This allows for
|
61
|
+
# automatically customizable spec types.
|
62
|
+
#
|
63
|
+
# See: register_spec_type and spec_type
|
64
|
+
|
65
|
+
TYPES = [[//, MiniTest::Spec]]
|
66
|
+
|
67
|
+
##
|
68
|
+
# Register a new type of spec that matches the spec's description.
|
69
|
+
# This method can take either a Regexp and a spec class or a spec
|
70
|
+
# class and a block that takes the description and returns true if
|
71
|
+
# it matches.
|
72
|
+
#
|
73
|
+
# Eg:
|
74
|
+
#
|
75
|
+
# register_spec_type(/Controller$/, MiniTest::Spec::Rails)
|
76
|
+
#
|
77
|
+
# or:
|
78
|
+
#
|
79
|
+
# register_spec_type(MiniTest::Spec::RailsModel) do |desc|
|
80
|
+
# desc.superclass == ActiveRecord::Base
|
81
|
+
# end
|
82
|
+
|
83
|
+
def self.register_spec_type(*args, &block)
|
84
|
+
if block then
|
85
|
+
matcher, klass = block, args.first
|
86
|
+
else
|
87
|
+
matcher, klass = *args
|
88
|
+
end
|
89
|
+
TYPES.unshift [matcher, klass]
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Figure out the spec class to use based on a spec's description. Eg:
|
94
|
+
#
|
95
|
+
# spec_type("BlahController") # => MiniTest::Spec::Rails
|
96
|
+
|
97
|
+
def self.spec_type desc
|
98
|
+
TYPES.find { |matcher, klass|
|
99
|
+
if matcher.respond_to? :call then
|
100
|
+
matcher.call desc
|
101
|
+
else
|
102
|
+
matcher === desc.to_s
|
103
|
+
end
|
104
|
+
}.last
|
105
|
+
end
|
106
|
+
|
107
|
+
@@describe_stack = []
|
108
|
+
def self.describe_stack # :nodoc:
|
109
|
+
@@describe_stack
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Returns the children of this spec.
|
114
|
+
|
115
|
+
def self.children
|
116
|
+
@children ||= []
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.nuke_test_methods! # :nodoc:
|
120
|
+
self.public_instance_methods.grep(/^test_/).each do |name|
|
121
|
+
self.send :undef_method, name
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Define a 'before' action. Inherits the way normal methods should.
|
127
|
+
#
|
128
|
+
# NOTE: +type+ is ignored and is only there to make porting easier.
|
129
|
+
#
|
130
|
+
# Equivalent to MiniTest::Unit::TestCase#setup.
|
131
|
+
|
132
|
+
def self.before type = :each, &block
|
133
|
+
raise "unsupported before type: #{type}" unless type == :each
|
134
|
+
|
135
|
+
add_setup_hook {|tc| tc.instance_eval(&block) }
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Define an 'after' action. Inherits the way normal methods should.
|
140
|
+
#
|
141
|
+
# NOTE: +type+ is ignored and is only there to make porting easier.
|
142
|
+
#
|
143
|
+
# Equivalent to MiniTest::Unit::TestCase#teardown.
|
144
|
+
|
145
|
+
def self.after type = :each, &block
|
146
|
+
raise "unsupported after type: #{type}" unless type == :each
|
147
|
+
|
148
|
+
add_teardown_hook {|tc| tc.instance_eval(&block) }
|
149
|
+
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# Define an expectation with name +desc+. Name gets morphed to a
|
153
|
+
# proper test method name. For some freakish reason, people who
|
154
|
+
# write specs don't like class inheritence, so this goes way out of
|
155
|
+
# its way to make sure that expectations aren't inherited.
|
156
|
+
#
|
157
|
+
# This is also aliased to #specify and doesn't require a +desc+ arg.
|
158
|
+
#
|
159
|
+
# Hint: If you _do_ want inheritence, use minitest/unit. You can mix
|
160
|
+
# and match between assertions and expectations as much as you want.
|
161
|
+
|
162
|
+
def self.it desc = "anonymous", &block
|
163
|
+
block ||= proc { skip "(no tests defined)" }
|
164
|
+
|
165
|
+
@specs ||= 0
|
166
|
+
@specs += 1
|
167
|
+
|
168
|
+
name = "test_%04d_%s" % [ @specs, desc.gsub(/\W+/, '_').downcase ]
|
169
|
+
|
170
|
+
define_method name, &block
|
171
|
+
|
172
|
+
self.children.each do |mod|
|
173
|
+
mod.send :undef_method, name if mod.public_method_defined? name
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# Essentially, define an accessor for +name+ with +block+.
|
179
|
+
#
|
180
|
+
# Why use let instead of def? I honestly don't know.
|
181
|
+
|
182
|
+
def self.let name, &block
|
183
|
+
define_method name do
|
184
|
+
@_memoized ||= {}
|
185
|
+
@_memoized.fetch(name) { |k| @_memoized[k] = instance_eval(&block) }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# Another lazy man's accessor generator. Made even more lazy by
|
191
|
+
# setting the name for you to +subject+.
|
192
|
+
|
193
|
+
def self.subject &block
|
194
|
+
let :subject, &block
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.create name, desc # :nodoc:
|
198
|
+
cls = Class.new(self) do
|
199
|
+
@name = name
|
200
|
+
@desc = desc
|
201
|
+
|
202
|
+
nuke_test_methods!
|
203
|
+
end
|
204
|
+
|
205
|
+
children << cls
|
206
|
+
|
207
|
+
cls
|
208
|
+
end
|
209
|
+
|
210
|
+
def self.to_s # :nodoc:
|
211
|
+
defined?(@name) ? @name : super
|
212
|
+
end
|
213
|
+
|
214
|
+
# :stopdoc:
|
215
|
+
def after_setup
|
216
|
+
run_setup_hooks
|
217
|
+
end
|
218
|
+
|
219
|
+
def before_teardown
|
220
|
+
run_teardown_hooks
|
221
|
+
end
|
222
|
+
|
223
|
+
class << self
|
224
|
+
attr_reader :desc
|
225
|
+
alias :specify :it
|
226
|
+
alias :name :to_s
|
227
|
+
end
|
228
|
+
# :startdoc:
|
229
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require 'partial_minispec'
|
4
|
+
|
5
|
+
# include the gem
|
6
|
+
require 'redis-expiring-set'
|
7
|
+
|
8
|
+
# Use database 15 for testing, so we don't risk overwriting any data that's
|
9
|
+
# actually useful
|
10
|
+
def clear_test_db
|
11
|
+
Redis.new(:db => 15).flushdb
|
12
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'redis-expiring-set/monkeypatch'
|
3
|
+
|
4
|
+
describe Redis::ExpiringSet do
|
5
|
+
describe "newly instantiated" do
|
6
|
+
before do
|
7
|
+
@redis = Redis.new(:db => 15)
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
clear_test_db
|
12
|
+
end
|
13
|
+
|
14
|
+
it "allows things to be added to expiration sets using xadd" do
|
15
|
+
assert_equal true, @redis.xadd("some_set", "some_value", Time.now + 10)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allows expiration to set in terms of seconds as well" do
|
19
|
+
assert_equal true, @redis.xadd("some_set", "some_value", 60)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "allows items to be removed from expiration sets using xrem" do
|
23
|
+
assert_equal true, @redis.xadd("some_set", "some_value", 60)
|
24
|
+
assert_equal true, @redis.xrem("some_set", "some_value")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "allows for all items in an expiration set to be returned by xmembers" do
|
28
|
+
assert_equal true, @redis.xadd("some_set", "some_value", 300)
|
29
|
+
assert_equal ["some_value"], @redis.xmembers("some_set")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "doesn't include any already expired items in xmembers" do
|
33
|
+
assert_equal true, @redis.xadd("some_set", "some_value", Time.now - 100)
|
34
|
+
assert_equal [], @redis.xmembers("some_set")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "allows for a count of items in an expiration set using xcard" do
|
38
|
+
assert_equal 0, @redis.xcard("some_set")
|
39
|
+
assert_equal true, @redis.xadd("some_set", "some_value", 60)
|
40
|
+
assert_equal 1, @redis.xcard("some_set")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Redis::ExpiringSet do
|
4
|
+
describe "newly instantiated" do
|
5
|
+
before do
|
6
|
+
@redis = Redis.new(:db => 15)
|
7
|
+
@expset = Redis::ExpiringSet.new(@redis)
|
8
|
+
end
|
9
|
+
|
10
|
+
after do
|
11
|
+
clear_test_db
|
12
|
+
end
|
13
|
+
|
14
|
+
it "allows things to be added to expiration sets using xadd" do
|
15
|
+
assert_equal true, @expset.xadd("some_set", "some_value", Time.now + 10)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allows expiration to set in terms of seconds as well" do
|
19
|
+
assert_equal true, @expset.xadd("some_set", "some_value", 60)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "allows items to be removed from expiration sets using xrem" do
|
23
|
+
assert_equal true, @expset.xadd("some_set", "some_value", 60)
|
24
|
+
assert_equal true, @expset.xrem("some_set", "some_value")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "allows for all items in an expiration set to be returned by xmembers" do
|
28
|
+
assert_equal true, @expset.xadd("some_set", "some_value", 300)
|
29
|
+
assert_equal ["some_value"], @expset.xmembers("some_set")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "doesn't include any already expired items in xmembers" do
|
33
|
+
assert_equal true, @expset.xadd("some_set", "some_value", Time.now - 100)
|
34
|
+
assert_equal [], @expset.xmembers("some_set")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "allows for a count of items in an expiration set using xcard" do
|
38
|
+
assert_equal 0, @expset.xcard("some_set")
|
39
|
+
assert_equal true, @expset.xadd("some_set", "some_value", 60)
|
40
|
+
assert_equal 1, @expset.xcard("some_set")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redis-expiring-set
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tommy Morgan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redis
|
16
|
+
requirement: &70159947898960 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70159947898960
|
25
|
+
description: Add some commands to Redis that make it easy to work with a set of objects
|
26
|
+
that need to expire individually.
|
27
|
+
email:
|
28
|
+
- tommy.morgan@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- .gitignore
|
34
|
+
- Gemfile
|
35
|
+
- README.markdown
|
36
|
+
- Rakefile
|
37
|
+
- lib/redis-expiring-set.rb
|
38
|
+
- lib/redis-expiring-set/monkeypatch.rb
|
39
|
+
- lib/redis-expiring-set/version.rb
|
40
|
+
- redis-expiring-set.gemspec
|
41
|
+
- test/partial_minispec.rb
|
42
|
+
- test/test_helper.rb
|
43
|
+
- test/unit/test_monkeypatched.rb
|
44
|
+
- test/unit/test_namespaced.rb
|
45
|
+
homepage: http://github.com/duwanis/redis-expiring-set
|
46
|
+
licenses: []
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project: redis-expiring-set
|
65
|
+
rubygems_version: 1.8.15
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Add an expiring set to redis.
|
69
|
+
test_files:
|
70
|
+
- test/partial_minispec.rb
|
71
|
+
- test/test_helper.rb
|
72
|
+
- test/unit/test_monkeypatched.rb
|
73
|
+
- test/unit/test_namespaced.rb
|