fresh_redis 0.0.8 → 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/Gemfile.lock +2 -2
- data/lib/fresh_redis.rb +2 -0
- data/lib/fresh_redis/set.rb +48 -0
- data/lib/fresh_redis/version.rb +1 -1
- data/spec/fresh_redis/set_spec.rb +67 -0
- metadata +16 -33
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3a33a93d584d6db65a64b944214c598237f47170
|
4
|
+
data.tar.gz: 3f6fc12ed9e25d33fb67f889b4a8b8dc42fa4e7f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 083f56e3c3c2e84877615e616501241130f27848094e34bcbe59ec03cd87e10a54f4e0f63572a44631a78e8351a4ff0cc383ca3edb45395460a7593c020126bc
|
7
|
+
data.tar.gz: cbeeace8ecf0d1944c9123181654aea4d114b7d6e9318b11dc8b13351e3eeade716c19ba60fa46e6fd45b03c0748b02c4737f9dd7f3c943933500ffa9969ade5
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
fresh_redis (0.0
|
4
|
+
fresh_redis (0.1.0)
|
5
5
|
redis
|
6
6
|
|
7
7
|
GEM
|
@@ -21,7 +21,7 @@ GEM
|
|
21
21
|
rb-fsevent (0.9.2)
|
22
22
|
rb-inotify (0.8.8)
|
23
23
|
ffi (>= 0.5.0)
|
24
|
-
redis (3.0
|
24
|
+
redis (3.2.0)
|
25
25
|
rspec (2.11.0)
|
26
26
|
rspec-core (~> 2.11.0)
|
27
27
|
rspec-expectations (~> 2.11.0)
|
data/lib/fresh_redis.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
class FreshRedis
|
2
|
+
module Set
|
3
|
+
|
4
|
+
def fsadd(key, value, options={})
|
5
|
+
key = build_key(key, options)
|
6
|
+
@redis.multi do
|
7
|
+
@redis.sadd(key.redis_key, value)
|
8
|
+
@redis.expire(key.redis_key, key.freshness)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def fsmembers(key, options={})
|
13
|
+
key = build_key(key, options)
|
14
|
+
|
15
|
+
bucket_values = @redis.pipelined {
|
16
|
+
key.timestamp_buckets.reverse.each do |bucket_key|
|
17
|
+
@redis.smembers(bucket_key)
|
18
|
+
end
|
19
|
+
}
|
20
|
+
|
21
|
+
# find the first non-nil value
|
22
|
+
bucket_values.flatten.uniq
|
23
|
+
end
|
24
|
+
|
25
|
+
def fsismembers(key, value, options={})
|
26
|
+
key = build_key(key, options)
|
27
|
+
|
28
|
+
bucket_values = @redis.pipelined {
|
29
|
+
key.timestamp_buckets.reverse.each do |bucket_key|
|
30
|
+
return true if @redis.sismembers(bucket_key, value)
|
31
|
+
end
|
32
|
+
}
|
33
|
+
|
34
|
+
# find the first non-nil value
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
def fsrem(key, value, options={})
|
39
|
+
key = build_key(key, options)
|
40
|
+
|
41
|
+
bucket_values = @redis.pipelined {
|
42
|
+
key.timestamp_buckets.reverse.each do |bucket_key|
|
43
|
+
@redis.srem(bucket_key, value)
|
44
|
+
end
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/fresh_redis/version.rb
CHANGED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'fresh_redis'
|
2
|
+
require 'mock_redis'
|
3
|
+
require 'timecop'
|
4
|
+
|
5
|
+
describe FreshRedis do
|
6
|
+
subject{ FreshRedis.new(mock_redis) }
|
7
|
+
let!(:mock_redis) { MockRedis.new }
|
8
|
+
let(:now) { Time.new(2012, 9, 27, 15, 40, 56, "+10:00") }
|
9
|
+
let(:normalized_now_minute) { Time.new(2012, 9, 27, 15, 40, 0, "+10:00") }
|
10
|
+
let(:normalized_one_minute_ago) { Time.new(2012, 9, 27, 15, 39, 0, "+10:00") }
|
11
|
+
let(:normalized_two_minutes_ago) { Time.new(2012, 9, 27, 15, 38, 0, "+10:00") }
|
12
|
+
let(:normalized_old) { Time.new(2012, 9, 27, 14, 38, 0, "+10:00") }
|
13
|
+
|
14
|
+
context "sets" do
|
15
|
+
before(:each) { Timecop.travel(now) }
|
16
|
+
after(:each) { Timecop.return }
|
17
|
+
|
18
|
+
describe "#fsadd" do
|
19
|
+
it "sets a value in a set for the normalized timestamp" do
|
20
|
+
Timecop.freeze(now - 3) { subject.fsadd "foo", "value" }
|
21
|
+
Timecop.freeze(now) { subject.fsadd "foo", "newer_value" }
|
22
|
+
Timecop.freeze(now - 60) { subject.fsadd "foo", "different_bucket" } # different normalized key
|
23
|
+
|
24
|
+
mock_redis.smembers("foo:#{normalized_now_minute.to_i}").should == ['newer_value', 'value']
|
25
|
+
end
|
26
|
+
|
27
|
+
it "sets a nil value ok" do
|
28
|
+
Timecop.freeze(now) { subject.fsadd "foo", nil }
|
29
|
+
|
30
|
+
mock_redis.smembers("foo:#{normalized_now_minute.to_i}").should == ['']
|
31
|
+
end
|
32
|
+
|
33
|
+
it "sets the freshness as the expiry" do
|
34
|
+
# relying on mock_redis's time handling here - which converts to/from using Time.now Possible flakey temporal breakage potential
|
35
|
+
subject.fsadd "foo", "baz", :freshness => 3600
|
36
|
+
|
37
|
+
mock_redis.ttl("foo:#{normalized_now_minute.to_i}").should == 3600
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#fsrem" do
|
42
|
+
it "removes the field from all timestamp buckets" do
|
43
|
+
Timecop.travel(now) { subject.fsadd "foo", "value" }
|
44
|
+
Timecop.travel(now) { subject.fsadd "foo", "don't touch" }
|
45
|
+
Timecop.travel(now - 60) { subject.fsadd "foo", "value" }
|
46
|
+
Timecop.travel(now - 60) { subject.fsadd "foo", "different_bucket" }
|
47
|
+
Timecop.travel(now - 60) { subject.fsadd "foo", "I shouldn't be returned" }
|
48
|
+
|
49
|
+
Timecop.travel(now) { subject.fsrem "foo", "value" } # Should only change the most recent bucket
|
50
|
+
|
51
|
+
mock_redis.smembers("foo:#{normalized_now_minute.to_i}").should == ["don't touch"]
|
52
|
+
mock_redis.smembers("foo:#{normalized_one_minute_ago.to_i}").should == ["I shouldn't be returned", "different_bucket"]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#fsmembers" do
|
57
|
+
it "gets the union of all members for the fields across timestamped buckets" do
|
58
|
+
mock_redis.sadd "foo:#{normalized_now_minute.to_i}", "notbar"
|
59
|
+
mock_redis.sadd "foo:#{normalized_one_minute_ago.to_i}", "bar"
|
60
|
+
mock_redis.sadd "foo:#{normalized_two_minutes_ago.to_i}", "bar"
|
61
|
+
|
62
|
+
subject.fsmembers("foo").should == ['notbar', 'bar']
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
metadata
CHANGED
@@ -1,52 +1,46 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fresh_redis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Julian Doherty (madlep)
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2015-01-25 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: redis
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rspec
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: mock_redis
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - '='
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,6 @@ dependencies:
|
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
52
|
- - '='
|
60
53
|
- !ruby/object:Gem::Version
|
@@ -62,7 +55,6 @@ dependencies:
|
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: guard-rspec
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
59
|
- - '='
|
68
60
|
- !ruby/object:Gem::Version
|
@@ -70,7 +62,6 @@ dependencies:
|
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
66
|
- - '='
|
76
67
|
- !ruby/object:Gem::Version
|
@@ -78,7 +69,6 @@ dependencies:
|
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: rake
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
73
|
- - '='
|
84
74
|
- !ruby/object:Gem::Version
|
@@ -86,7 +76,6 @@ dependencies:
|
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
80
|
- - '='
|
92
81
|
- !ruby/object:Gem::Version
|
@@ -94,7 +83,6 @@ dependencies:
|
|
94
83
|
- !ruby/object:Gem::Dependency
|
95
84
|
name: timecop
|
96
85
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
86
|
requirements:
|
99
87
|
- - '='
|
100
88
|
- !ruby/object:Gem::Version
|
@@ -102,7 +90,6 @@ dependencies:
|
|
102
90
|
type: :development
|
103
91
|
prerelease: false
|
104
92
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
93
|
requirements:
|
107
94
|
- - '='
|
108
95
|
- !ruby/object:Gem::Version
|
@@ -114,8 +101,8 @@ executables: []
|
|
114
101
|
extensions: []
|
115
102
|
extra_rdoc_files: []
|
116
103
|
files:
|
117
|
-
- .gitignore
|
118
|
-
- .rspec
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
119
106
|
- Gemfile
|
120
107
|
- Gemfile.lock
|
121
108
|
- Guardfile
|
@@ -126,45 +113,41 @@ files:
|
|
126
113
|
- lib/fresh_redis.rb
|
127
114
|
- lib/fresh_redis/hash.rb
|
128
115
|
- lib/fresh_redis/key.rb
|
116
|
+
- lib/fresh_redis/set.rb
|
129
117
|
- lib/fresh_redis/string.rb
|
130
118
|
- lib/fresh_redis/version.rb
|
131
119
|
- spec/fresh_redis/hash_spec.rb
|
132
120
|
- spec/fresh_redis/key_spec.rb
|
121
|
+
- spec/fresh_redis/set_spec.rb
|
133
122
|
- spec/fresh_redis/string_spec.rb
|
134
123
|
- spec/fresh_redis_spec.rb
|
135
124
|
homepage: ''
|
136
125
|
licenses: []
|
126
|
+
metadata: {}
|
137
127
|
post_install_message:
|
138
128
|
rdoc_options: []
|
139
129
|
require_paths:
|
140
130
|
- lib
|
141
131
|
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
-
none: false
|
143
132
|
requirements:
|
144
|
-
- -
|
133
|
+
- - ">="
|
145
134
|
- !ruby/object:Gem::Version
|
146
135
|
version: '0'
|
147
|
-
segments:
|
148
|
-
- 0
|
149
|
-
hash: -3222965477484950148
|
150
136
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
-
none: false
|
152
137
|
requirements:
|
153
|
-
- -
|
138
|
+
- - ">="
|
154
139
|
- !ruby/object:Gem::Version
|
155
140
|
version: '0'
|
156
|
-
segments:
|
157
|
-
- 0
|
158
|
-
hash: -3222965477484950148
|
159
141
|
requirements: []
|
160
142
|
rubyforge_project:
|
161
|
-
rubygems_version:
|
143
|
+
rubygems_version: 2.2.2
|
162
144
|
signing_key:
|
163
|
-
specification_version:
|
145
|
+
specification_version: 4
|
164
146
|
summary: Use redis for working with recent temporal based data that can expiry gradually.
|
165
147
|
Useful for things like "get a count all failed login attempts for the last hour"
|
166
148
|
test_files:
|
167
149
|
- spec/fresh_redis/hash_spec.rb
|
168
150
|
- spec/fresh_redis/key_spec.rb
|
151
|
+
- spec/fresh_redis/set_spec.rb
|
169
152
|
- spec/fresh_redis/string_spec.rb
|
170
153
|
- spec/fresh_redis_spec.rb
|