redness 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 HowAboutWe
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/RAKEFILE ADDED
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+ require 'rspec/core'
6
+ require 'rspec/core/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new(:spec) do |spec|
9
+ spec.pattern = FileList['spec/**/*_spec.rb']
10
+ end
11
+
12
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
13
+ spec.pattern = 'spec/**/*_spec.rb'
14
+ spec.rcov = true
15
+ end
16
+
17
+ task :default => :spec
data/README.rdoc ADDED
@@ -0,0 +1 @@
1
+ WIP
@@ -0,0 +1,35 @@
1
+ class Red
2
+ class RedisUnavailable < StandardError; end
3
+
4
+ def self.redis
5
+ $redis
6
+ end
7
+
8
+ def self.delete(key)
9
+ redis.del(key)
10
+ end
11
+
12
+ def redis
13
+ Red.redis
14
+ end
15
+
16
+ def execute_with_uncertainty(fail_return = [])
17
+ yield
18
+ rescue RedisUnavailable, Errno::ECONNREFUSED, Timeout, Timeout::Error, Errno::EAGAIN
19
+ fail_return
20
+ end
21
+
22
+ def multi_with_caution(fail_return = [])
23
+ redis.multi
24
+ yield
25
+ redis.exec
26
+ rescue
27
+ redis.discard
28
+ fail_return
29
+ end
30
+
31
+ def method_missing(method, *args)
32
+ raise RedisUnavailable unless redis
33
+ redis.send(method, *args)
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ class RedCappedList
2
+ attr_accessor :key, :cap
3
+
4
+ def initialize(key, cap)
5
+ self.key = key
6
+ self.cap = cap
7
+ end
8
+
9
+ def get
10
+ RedList.get(key)
11
+ end
12
+
13
+ def add(value)
14
+ RedList.add(key, value)
15
+ RedList.trim_to(key, cap)
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ class RedExpire < Red
2
+ def self.redis
3
+ @redis ||= Red.new
4
+ end
5
+
6
+ def self.set(key, expires_in)
7
+ redis.execute_with_uncertainty(nil) do
8
+ redis.expire(key, expires_in.seconds.to_i)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ class RedHash
2
+ attr_accessor :hash_key, :redis
3
+
4
+ def initialize(hash_key = self.class.name, redis = Red.new)
5
+ self.hash_key = hash_key
6
+ self.redis = redis
7
+ end
8
+
9
+ def []=(key, value)
10
+ redis.execute_with_uncertainty(nil) do
11
+ redis.hset(hash_key, key, value)
12
+ value
13
+ end
14
+ end
15
+
16
+ def [](key)
17
+ redis.execute_with_uncertainty(nil) do
18
+ redis.hget(hash_key, key)
19
+ end
20
+ end
21
+
22
+ def remove(key)
23
+ redis.execute_with_uncertainty(false) do
24
+ redis.hdel(hash_key, key)
25
+ end
26
+ end
27
+
28
+ def all
29
+ redis.execute_with_uncertainty({}) do
30
+ hash = redis.hgetall(hash_key)
31
+
32
+ HashWithIndifferentAccess.new(hash)
33
+ end
34
+ end
35
+
36
+ def clear
37
+ redis.execute_with_uncertainty(false) do
38
+ redis.hkeys(hash_key).each do |key|
39
+ redis.hdel(hash_key, key)
40
+ end
41
+ end
42
+ end
43
+
44
+ def set(options)
45
+ redis.execute_with_uncertainty(false) do
46
+ options.keys.each do |option|
47
+ self[option] = options[option]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ class RedJSON < Red
2
+ def self.redis
3
+ @redis ||= Red.new
4
+ end
5
+
6
+ def self.get(key)
7
+ redis.execute_with_uncertainty(nil) do
8
+ value = redis.get(key)
9
+
10
+ if value
11
+ JSON.parse(value)
12
+ end
13
+ end
14
+ end
15
+
16
+ def self.set(key, value)
17
+ redis.execute_with_uncertainty(nil) do
18
+ redis.set(key, value.to_json)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,50 @@
1
+ class RedList
2
+ def self.redis
3
+ @redis ||= Red.new
4
+ end
5
+
6
+ def self.get(key)
7
+ redis.execute_with_uncertainty([]) do
8
+ redis.lrange(key, 0, -1).map(&:to_i)
9
+ end
10
+ end
11
+
12
+ def self.get_strings(key)
13
+ redis.execute_with_uncertainty([]) do
14
+ redis.lrange(key, 0, -1)
15
+ end
16
+ end
17
+
18
+ def self.add(key, value)
19
+ redis.execute_with_uncertainty(false) do
20
+ redis.lpush(key, value)
21
+ end
22
+ end
23
+
24
+ def self.remove(key, value, occurrences = 0)
25
+ redis.execute_with_uncertainty(false) do
26
+ redis.lrem(key, occurrences, value)
27
+ end
28
+ end
29
+
30
+ def self.count(key, value)
31
+ get(key).count(value)
32
+ end
33
+
34
+ def self.total_size(key)
35
+ get(key).count
36
+ end
37
+
38
+ def self.trim_to(key, amount)
39
+ redis.execute_with_uncertainty(false) do
40
+ redis.ltrim(key, 0, (amount - 1))
41
+ end
42
+ end
43
+
44
+ def self.pop(key)
45
+ redis.execute_with_uncertainty(false) do
46
+ redis.rpop(key)
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,76 @@
1
+ class RedSet
2
+ def self.redis
3
+ @redis ||= Red.new
4
+ end
5
+
6
+ def self.add(key, member, options = {})
7
+ redis.execute_with_uncertainty do
8
+ redis.watch(key)
9
+
10
+ if redis.zrank(key, member).nil?
11
+ if options[:score] and options[:score].respond_to?(:call)
12
+ score = options[:score].call.to_i
13
+ else
14
+ score = redis.zcard(key).to_i
15
+ end
16
+
17
+ redis.multi_with_caution(false) do
18
+ redis.zadd(key, score.to_s, member)
19
+ end
20
+ end
21
+ end
22
+
23
+ ensure
24
+ redis.execute_with_uncertainty(0) do
25
+ redis.unwatch
26
+ end
27
+ end
28
+
29
+
30
+ def self.remove(key, member)
31
+ redis.execute_with_uncertainty do
32
+ redis.zrem(key, member)
33
+ end
34
+ end
35
+
36
+ def self.get(key, options = {})
37
+ lower_bound = options[:lower] || 0
38
+ upper_bound = options[:upper] || -1
39
+ with_scores = options[:with_scores] || false
40
+ scoring = options[:scoring] || lambda {|x| x}
41
+
42
+ redis.execute_with_uncertainty([]) do
43
+ results = redis.zrevrange(key, lower_bound, upper_bound, :with_scores => with_scores)
44
+ if with_scores
45
+ [].tap do |memo|
46
+ results.each_slice(2) do |slice|
47
+ memo << [slice[0].to_i, scoring.call(slice[1].to_i)]
48
+ end
49
+ end
50
+ else
51
+ results.map(&:to_i)
52
+ end
53
+ end
54
+ end
55
+
56
+ def self.get_strings(key, options = {})
57
+ lower_bound = options[:lower] || 0
58
+ upper_bound = options[:upper] || -1
59
+
60
+ redis.execute_with_uncertainty do
61
+ redis.zrevrange(key, lower_bound, upper_bound)
62
+ end
63
+ end
64
+
65
+ def self.count(key)
66
+ redis.execute_with_uncertainty(0) do
67
+ redis.zcard(key)
68
+ end
69
+ end
70
+
71
+ def self.score(key, member)
72
+ redis.execute_with_uncertainty(nil) do
73
+ redis.zscore(key, member)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,23 @@
1
+ class RedSetMulti
2
+ attr_accessor :keys, :redis
3
+
4
+ def initialize(*keys)
5
+ self.keys = keys
6
+ self.redis = Red.new
7
+ end
8
+
9
+ def get(options = {})
10
+ lower_bound = options[:lower] || 0
11
+ upper_bound = options[:upper] || -1
12
+
13
+ redis.execute_with_uncertainty([]) do
14
+ results = keys.map { |key| redis.zrevrange(key, lower_bound, upper_bound) }
15
+
16
+ if results.present?
17
+ results.map {|r| r.map(&:to_i)}
18
+ else
19
+ []
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ class RedSetUnion
2
+ attr_accessor :keys, :redis
3
+
4
+ def initialize(*keys)
5
+ self.keys = keys
6
+ self.redis = Red.new
7
+
8
+ @temporary_key = "tmp:red_set_union"
9
+ end
10
+
11
+ def get(options = {})
12
+ lower_bound = options[:lower] || 0
13
+ upper_bound = options[:upper] || -1
14
+
15
+ redis.execute_with_uncertainty([]) do
16
+ results = redis.multi_with_caution do
17
+ redis.zunionstore(@temporary_key, keys)
18
+ redis.zrevrange(@temporary_key, lower_bound, upper_bound)
19
+ redis.del(@temporary_key)
20
+ end
21
+
22
+ if results.present?
23
+ results[1].flatten.compact.uniq.map(&:to_i)
24
+ else
25
+ []
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ class TimedRedSet < RedSet
2
+ def self.add(key, member)
3
+ super(key, member, :score => lambda { Precisionable.int_from_float(Time.now) })
4
+ end
5
+
6
+ def self.since(key, start, options={})
7
+ precise_start = Precisionable.int_from_float(start)
8
+
9
+ redis.execute_with_uncertainty([]) do
10
+ if options[:upper] and options[:lower]
11
+ lower = options[:lower].to_i
12
+ upper = options[:upper].to_i
13
+
14
+ redis.zrevrangebyscore(key, "+inf", precise_start)[lower..upper].map(&:to_i)
15
+ else
16
+ redis.zrevrangebyscore(key, "+inf", precise_start).map(&:to_i)
17
+ end
18
+ end
19
+ end
20
+
21
+ def self.score(key, member)
22
+ precise_time = super
23
+ Time.at(Precisionable.float_from_int(precise_time)) if precise_time
24
+ end
25
+
26
+ def self.get_with_timestamps(key, options = {})
27
+ get(key, options.merge({
28
+ :with_scores => true,
29
+ :scoring => lambda {|x| Time.at(Precisionable.float_from_int(x))}
30
+ })
31
+ )
32
+ end
33
+ end
data/lib/redness.rb ADDED
@@ -0,0 +1,10 @@
1
+ require "redness/red"
2
+ require "redness/red_capped_list"
3
+ require "redness/red_expire"
4
+ require "redness/red_hash"
5
+ require "redness/red_json"
6
+ require "redness/red_list"
7
+ require "redness/red_set"
8
+ require "redness/red_set_multi"
9
+ require "redness/red_set_union"
10
+ require "redness/timed_red_set"
data/lib/version.rb ADDED
@@ -0,0 +1,4 @@
1
+ module Redness
2
+ VERSION="0.0.1"
3
+ MAJOR, MINOR, TINY = VERSION.split(".")
4
+ end
data/redness.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "redness"
6
+ s.version = Redness::VERSION
7
+ s.authors = %w(
8
+ Baldur Gudbjornsson
9
+ Matt Vermaak
10
+ Bryan Woods
11
+ Kenneth Lay
12
+ Marco Carag
13
+ Conner Peirce
14
+ )
15
+ s.email = "dev@howaboutwe.com"
16
+ s.summary = "Helpful Ruby classes for improved Redis data structures"
17
+ s.description = "Helpful Ruby classes for improved Redis data structures"
18
+ s.homepage = "http://github.com/howaboutwe/redness"
19
+ s.licenses = ["MIT"]
20
+ s.files = `git ls-files`.split("\n")
21
+ s.add_dependency "rspec"
22
+ s.add_dependency "redis", "~> 2.2.2"
23
+ s.add_dependency "active_support"
24
+ s.add_dependency "timecop"
25
+ s.require_paths = ["lib"]
26
+ end
@@ -0,0 +1,11 @@
1
+ module Precisionable
2
+ PRECISION = 100000.0
3
+
4
+ def self.int_from_float(float)
5
+ (float.to_f * PRECISION).round
6
+ end
7
+
8
+ def self.float_from_int(int)
9
+ (int.to_f / PRECISION)
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ class RedisRunner
2
+ def self.start
3
+ fork do
4
+ `cd tmp && echo port #{port} | redis-server -`
5
+ at_exit { exit! }
6
+ end
7
+ end
8
+
9
+ def self.stop
10
+ `redis-cli -h #{host} -p #{port} 'SHUTDOWN'`
11
+
12
+ "#{host}:#{port}"
13
+ end
14
+
15
+ def self.up?
16
+ system("redis-cli -h #{host} -p #{port} 'INFO' 2&>1 > /dev/null")
17
+ $? == 0
18
+ end
19
+
20
+ private
21
+
22
+ def self.host
23
+ "localhost"
24
+ end
25
+
26
+ def self.port
27
+ "6379"
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe RedCappedList do
4
+ describe ".new" do
5
+ it "returns an instance of RedCappedList with cap and key set" do
6
+ r = RedCappedList.new("somekey", 1000)
7
+ r.key.should == "somekey"
8
+ r.cap.should == 1000
9
+ end
10
+ end
11
+
12
+ describe "#get" do
13
+ it "returns the list at the key" do
14
+ r = RedCappedList.new("somekey", 1000)
15
+ r.get.should == []
16
+ end
17
+ end
18
+
19
+ describe "#add" do
20
+ it "adds to the list at the key" do
21
+ r = RedCappedList.new("somekey", 1000)
22
+ r.add(1)
23
+ r.get.should == [1]
24
+ end
25
+
26
+ it "keeps the collection capped at the cap" do
27
+ r = RedCappedList.new("somekey", 1)
28
+ r.add(1)
29
+ r.get.should == [1]
30
+ r.add(2)
31
+ r.get.should == [2]
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe RedExpire do
4
+ describe ".set" do
5
+ it "expires the key in the given seconds" do
6
+ RedJSON.set('test', 1)
7
+ RedExpire.set('test', 0.seconds)
8
+ RedJSON.get('test').should be_nil
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe RedHash do
4
+ let(:red_hash) { RedHash.new("configuration") }
5
+
6
+ describe ".new" do
7
+ it "returns a red hash whose hash key is set to the first parameter" do
8
+ r = RedHash.new("test")
9
+ r.hash_key.should == "test"
10
+ end
11
+
12
+ it "uses the class name as the default hash key if no parameter is passed" do
13
+ r = RedHash.new
14
+ r.hash_key.should == "RedHash"
15
+ end
16
+ end
17
+
18
+ describe "#[]=" do
19
+ it "sets a key" do
20
+ red_hash[:key] = "value"
21
+ red_hash[:key].should == "value"
22
+ end
23
+ end
24
+
25
+ describe "#[]" do
26
+ it "gets a key" do
27
+ red_hash[:key] = "value"
28
+ red_hash[:key].should == "value"
29
+ end
30
+
31
+ it "returns nil if the key does not exist" do
32
+ red_hash[:non_key].should be_nil
33
+ end
34
+
35
+ it "should get different values depending on hash_key" do
36
+ red_hash[:key] = "value"
37
+
38
+ another_hash = RedHash.new("another_hash")
39
+ another_hash[:key] = "value2"
40
+
41
+ red_hash[:key].should == "value"
42
+ another_hash[:key].should == "value2"
43
+ end
44
+ end
45
+
46
+ describe "#all" do
47
+ it "gets the entire hash" do
48
+ red_hash[:key] = "value"
49
+ red_hash[:another_key] = "another_value"
50
+
51
+ red_hash.all.should == {
52
+ "key" => "value",
53
+ "another_key" => "another_value"
54
+ }
55
+ end
56
+ end
57
+
58
+ describe "#remove" do
59
+ it "removes a key" do
60
+ red_hash[:key] = "value"
61
+ red_hash[:key].should == "value"
62
+
63
+ red_hash.remove(:key)
64
+ red_hash[:key].should be_nil
65
+ end
66
+ end
67
+
68
+ describe "#clear" do
69
+ it "removes all keys" do
70
+ red_hash[:key] = "value"
71
+ red_hash[:another_key] = "another_value"
72
+
73
+ red_hash.clear
74
+ red_hash.all.should == {}
75
+ end
76
+ end
77
+
78
+ describe "#set" do
79
+ it "accepts a hash, and sets all keys" do
80
+ red_hash.set(
81
+ :key => "something",
82
+ :another_key => "something_else"
83
+ )
84
+
85
+ red_hash["another_key"].should == "something_else"
86
+ red_hash["key"].should == "something"
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe RedJSON do
4
+ describe ".set and .get" do
5
+ it "stores json at the provided key" do
6
+ RedJSON.set("test", [1,2,3])
7
+ RedJSON.get("test").should == [1,2,3]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe RedList do
4
+ describe ".add" do
5
+ it "adds a member to the list" do
6
+ RedList.add("somekey", 2)
7
+ list = RedList.get("somekey")
8
+
9
+ list.should == [2]
10
+ end
11
+
12
+ it "allows duplicates in the list" do
13
+ RedList.add("somekey", 2)
14
+ RedList.add("somekey", 2)
15
+
16
+ list = RedList.get("somekey")
17
+ list.should == [2,2]
18
+ end
19
+ end
20
+
21
+ describe ".remove" do
22
+ it "removes all matching values in list" do
23
+ RedList.add("somekey", 2)
24
+ RedList.add("somekey", 3)
25
+ RedList.add("somekey", 2)
26
+
27
+ RedList.remove("somekey", 2)
28
+ list = RedList.get("somekey")
29
+ list.should == [3]
30
+ end
31
+
32
+ context "supplied count" do
33
+ it "removes first {count} occurrences of value in list" do
34
+ RedList.add("somekey", 2)
35
+ RedList.add("somekey", 3)
36
+ RedList.add("somekey", 2)
37
+
38
+ RedList.remove("somekey", 2, 1)
39
+ list = RedList.get("somekey")
40
+ list.should == [3, 2]
41
+ end
42
+ end
43
+ end
44
+
45
+ describe ".get" do
46
+ it "returns the list in that key" do
47
+ RedList.add("somekey", 1)
48
+ RedList.add("somekey", 2)
49
+
50
+ RedList.get("somekey").should == [2,1]
51
+ end
52
+ end
53
+
54
+ describe ".count" do
55
+ it "returns the number of occurrences of the value within the list" do
56
+ RedList.add("somekey", 1)
57
+ RedList.add("somekey", 1)
58
+ RedList.add("somekey", 2)
59
+
60
+ RedList.count("somekey", 1).should == 2
61
+ end
62
+ end
63
+
64
+ describe ".total_size" do
65
+ it "retuns the total size of the array (list)" do
66
+ RedList.total_size("foobar").should == 0
67
+ RedList.add("foobar","a")
68
+ RedList.total_size("foobar").should == 1
69
+ RedList.add("foobar","b")
70
+ RedList.total_size("foobar").should == 2
71
+ end
72
+ end
73
+
74
+ describe ".pop" do
75
+ it "removes the last element in the list" do
76
+ RedList.add("foobar2", "a")
77
+ RedList.add("foobar2", "b")
78
+ RedList.add("foobar2", "c")
79
+
80
+ RedList.redis.lrange("foobar2", 0, -1).should == ["c", "b", "a"]
81
+ RedList.pop("foobar2").should == "a"
82
+ RedList.redis.lrange("foobar2", 0, -1).should == ["c", "b"]
83
+ end
84
+ end
85
+
86
+ describe ".trim_to" do
87
+ it "removes elements from the list until {amount} remain" do
88
+ RedList.add("somekey", 1)
89
+ RedList.add("somekey", 2)
90
+ RedList.add("somekey", 3)
91
+
92
+ RedList.trim_to("somekey", 1)
93
+ RedList.get("somekey").should == [3]
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe RedSetMulti do
4
+ describe "#new" do
5
+ it "returns an instance of RedSetMulti with keys set" do
6
+ r = RedSetMulti.new("key1", "key2")
7
+ r.keys.should == ["key1", "key2"]
8
+ end
9
+ end
10
+
11
+ describe "#get" do
12
+ context "RedSet" do
13
+ before do
14
+ RedSet.add("key1", 1)
15
+ RedSet.add("key1", 2)
16
+ RedSet.add("key1", 3)
17
+
18
+ RedSet.add("key2", 1)
19
+ RedSet.add("key2", 6)
20
+ RedSet.add("key2", 7)
21
+
22
+ RedSet.add("key3", 9)
23
+ end
24
+
25
+ it "returns an array with all of the unique values ordered by score" do
26
+ r = RedSetMulti.new("key1", "key2", "key3")
27
+ r.get.should == [[3,2,1],[7,6,1],[9]]
28
+ end
29
+
30
+ context "with non existent key" do
31
+ it "does not return nils" do
32
+ r = RedSetMulti.new("key1", "key2", "key3", "nonexistent")
33
+ r.get.should == [[3,2,1],[7,6,1],[9],[]]
34
+ end
35
+ end
36
+ end
37
+
38
+ context "TimedRedSet" do
39
+ before do
40
+ TimedRedSet.add("key1", 1)
41
+ TimedRedSet.add("key1", 2)
42
+ TimedRedSet.add("key1", 3)
43
+
44
+ TimedRedSet.add("key2", 1)
45
+ TimedRedSet.add("key2", 6)
46
+ TimedRedSet.add("key2", 7)
47
+
48
+ TimedRedSet.add("key3", 9)
49
+ end
50
+
51
+ it "returns an array with all of the unique values ordered by score" do
52
+ r = RedSetMulti.new("key1", "key2", "key3")
53
+ r.get.should == [[3,2,1],[7,6,1],[9]]
54
+ end
55
+
56
+ context "with non existent key" do
57
+ it "does not return nils" do
58
+ r = RedSetMulti.new("key1", "nonexistent", "key2", "key3")
59
+ r.get.should == [[3,2,1],[],[7,6,1],[9]]
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe RedSet do
4
+ describe ".add" do
5
+ it "should add a member to the set" do
6
+ RedSet.add("somekey", 2)
7
+ set = RedSet.get("somekey")
8
+
9
+ set.should == [2]
10
+ end
11
+
12
+ it "should not add duplicates to the set" do
13
+ RedSet.add("somekey", 2)
14
+ RedSet.add("somekey", 2)
15
+
16
+ set = RedSet.get("somekey")
17
+
18
+ set.should == [2]
19
+ end
20
+
21
+ it "should not change the order of the set on attempted duplicate" do
22
+ RedSet.add("somekey", 2)
23
+ RedSet.add("somekey", 3)
24
+ RedSet.add("somekey", 2)
25
+
26
+ set = RedSet.get("somekey")
27
+
28
+ set.should == [3,2]
29
+ end
30
+
31
+ it "sets the score when one is provided" do
32
+ RedSet.add("somekey", 1, :score => lambda { 1000 })
33
+
34
+ RedSet.score("somekey", 1).should == "1000"
35
+ end
36
+ end
37
+
38
+ describe ".remove" do
39
+ it "should remove a member from the set" do
40
+ RedSet.add("somekey", 1)
41
+ RedSet.add("somekey", 2)
42
+
43
+ RedSet.remove("somekey", 2)
44
+
45
+ RedSet.get("somekey").should == [1]
46
+ end
47
+ end
48
+
49
+ describe ".score" do
50
+ it "returns the score of member within key" do
51
+ RedSet.add("somekey", 1)
52
+
53
+ RedSet.score("somekey", 1).should == "0"
54
+ end
55
+ end
56
+
57
+ describe ".get" do
58
+ describe "pagination" do
59
+ it "should return results bounded by provided options" do
60
+ RedSet.add("somekey", 1)
61
+ RedSet.add("somekey", 2)
62
+
63
+ RedSet.get("somekey", :lower => 0, :upper => 0).should == [2]
64
+ RedSet.get("somekey", :lower => 1, :upper => 1).should == [1]
65
+ end
66
+ end
67
+ end
68
+
69
+ describe ".count" do
70
+ it "should return the total count" do
71
+ 7.times {|i| RedSet.add("somekey",i)}
72
+
73
+ RedSet.count("somekey").should == 7
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe RedSetUnion do
4
+ describe ".new" do
5
+ it "returns an instance of RedMultiSet with keys set" do
6
+ r = RedSetUnion.new("key1", "key2", "key3")
7
+ r.keys.should == ["key1", "key2", "key3"]
8
+ end
9
+ end
10
+
11
+ describe "#get" do
12
+ context "RedSet" do
13
+ before do
14
+ RedSet.add("key1", 1)
15
+ RedSet.add("key1", 2)
16
+ RedSet.add("key1", 3)
17
+
18
+ RedSet.add("key2", 1)
19
+ RedSet.add("key2", 6)
20
+ RedSet.add("key2", 7)
21
+
22
+ RedSet.add("key3", 9)
23
+ end
24
+
25
+ it "returns an array with all of the unique values ordered by score" do
26
+ r = RedSetUnion.new("key1", "key2", "key3")
27
+ r.get.should == [7,3,6,2,9,1]
28
+ end
29
+
30
+ context "with non existent key" do
31
+ it "does not return nils" do
32
+ r = RedSetUnion.new("key1", "key2", "key3", "nonexistent")
33
+ r.get.should == [7,3,6,2,9,1]
34
+ end
35
+ end
36
+ end
37
+
38
+ context "TimedRedSet" do
39
+ before do
40
+ TimedRedSet.add("key1", 1)
41
+ TimedRedSet.add("key1", 2)
42
+ TimedRedSet.add("key1", 3)
43
+
44
+ TimedRedSet.add("key2", 1)
45
+ TimedRedSet.add("key2", 6)
46
+ TimedRedSet.add("key2", 7)
47
+
48
+ TimedRedSet.add("key3", 9)
49
+ end
50
+
51
+ it "returns an array with all of the unique values ordered by score" do
52
+ r = RedSetUnion.new("key1", "key2", "key3")
53
+ r.get.should == [1,9,7,6,3,2]
54
+ end
55
+
56
+ context "with non existent key" do
57
+ it "does not return nils" do
58
+ r = RedSetUnion.new("key1", "nonexistent", "key2", "key3")
59
+ r.get.should == [1,9,7,6,3,2]
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_integration_helper'
2
+
3
+ describe TimedRedSet do
4
+ describe ".add" do
5
+ it "adds a member to the set" do
6
+ TimedRedSet.add("somekey", 1)
7
+
8
+ set = TimedRedSet.get("somekey")
9
+
10
+ set.should == [1]
11
+ end
12
+
13
+ it "adds a member to the set with a timestamp for score" do
14
+ the_time = Time.now
15
+ Timecop.freeze(the_time) do
16
+ TimedRedSet.add("somekey", 1)
17
+
18
+ TimedRedSet.score("somekey", 1).to_s.should == the_time.to_s
19
+ end
20
+ end
21
+ end
22
+
23
+ describe ".since" do
24
+ it "returns the additions since a start time" do
25
+ the_past = 2.days.ago
26
+
27
+ Timecop.freeze(the_past) do
28
+ TimedRedSet.add("somekey", 1)
29
+ end
30
+
31
+ TimedRedSet.add("somekey", 2)
32
+
33
+ TimedRedSet.since("somekey", 1.day.ago).should == [2]
34
+ end
35
+
36
+ it "returns results sorted in reverse chronological order" do
37
+ the_past = 2.days.ago
38
+
39
+ Timecop.freeze(the_past) do
40
+ TimedRedSet.add("somekey", 1)
41
+ end
42
+
43
+ TimedRedSet.add("somekey", 2)
44
+
45
+ TimedRedSet.since("somekey", 3.days.ago).should == [2, 1]
46
+ end
47
+
48
+ it "accepts an upper and lower bound" do
49
+ TimedRedSet.add("somekey", 1)
50
+ TimedRedSet.add("somekey", 2)
51
+ TimedRedSet.add("somekey", 3)
52
+
53
+ TimedRedSet.since("somekey", 1.day.ago, :lower => 0, :upper => 0).should == [3]
54
+ TimedRedSet.since("somekey", 1.day.ago, :lower => 0, :upper => 1).should == [3,2]
55
+ TimedRedSet.since("somekey", 1.day.ago, :lower => 1, :upper => 2).should == [2,1]
56
+ end
57
+ end
58
+
59
+ describe ".get_with_timestamps" do
60
+ it "returns members of the set with times" do
61
+ TimedRedSet.add("somekey", 1)
62
+ TimedRedSet.add("somekey", 2)
63
+ TimedRedSet.add("somekey", 3)
64
+
65
+ TimedRedSet.get_with_timestamps("somekey").should == [
66
+ [3, TimedRedSet.score("somekey", 3)],
67
+ [2, TimedRedSet.score("somekey", 2)],
68
+ [1, TimedRedSet.score("somekey", 1)]
69
+ ]
70
+ end
71
+
72
+ it "supports pagination" do
73
+ TimedRedSet.add("somekey", 1)
74
+ TimedRedSet.add("somekey", 2)
75
+ TimedRedSet.add("somekey", 3)
76
+
77
+ TimedRedSet.get_with_timestamps("somekey", :lower => 0, :upper => 0).should == [
78
+ [3, TimedRedSet.score("somekey", 3)]
79
+ ]
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,48 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'rspec'
5
+ require 'redis'
6
+ require 'active_support/core_ext'
7
+ require 'timecop'
8
+
9
+ require 'redis_runner'
10
+ require 'precisionable'
11
+
12
+ require 'redness'
13
+
14
+ RSpec.configure do |config|
15
+ config.before(:suite) do
16
+ print "Starting Redis..."
17
+
18
+ RedisRunner.start
19
+
20
+ attempts = 10
21
+
22
+ until RedisRunner.up? or attempts <= 0
23
+ sleep 1
24
+ attempts -= 1
25
+ end
26
+
27
+ if RedisRunner.up?
28
+ puts " OK"
29
+ else
30
+ puts " FAIL"
31
+ end
32
+
33
+ $redis = Redis.new(
34
+ host: RedisRunner.host,
35
+ port: RedisRunner.port,
36
+ thread_safe: true
37
+ )
38
+ end
39
+
40
+ config.after(:suite) do
41
+ RedisRunner.stop
42
+ end
43
+
44
+ config.after(:each) do
45
+ Timecop.return
46
+ $redis.flushall
47
+ end
48
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redness
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Baldur
9
+ - Gudbjornsson
10
+ - Matt
11
+ - Vermaak
12
+ - Bryan
13
+ - Woods
14
+ - Kenneth
15
+ - Lay
16
+ - Marco
17
+ - Carag
18
+ - Conner
19
+ - Peirce
20
+ autorequire:
21
+ bindir: bin
22
+ cert_chain: []
23
+ date: 2012-04-13 00:00:00.000000000Z
24
+ dependencies:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70292141275900 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70292141275900
36
+ - !ruby/object:Gem::Dependency
37
+ name: redis
38
+ requirement: &70292141296880 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 2.2.2
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70292141296880
47
+ - !ruby/object:Gem::Dependency
48
+ name: active_support
49
+ requirement: &70292141296460 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70292141296460
58
+ - !ruby/object:Gem::Dependency
59
+ name: timecop
60
+ requirement: &70292141296000 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70292141296000
69
+ description: Helpful Ruby classes for improved Redis data structures
70
+ email: dev@howaboutwe.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - .gitignore
76
+ - LICENSE.txt
77
+ - RAKEFILE
78
+ - README.rdoc
79
+ - lib/redness.rb
80
+ - lib/redness/red.rb
81
+ - lib/redness/red_capped_list.rb
82
+ - lib/redness/red_expire.rb
83
+ - lib/redness/red_hash.rb
84
+ - lib/redness/red_json.rb
85
+ - lib/redness/red_list.rb
86
+ - lib/redness/red_set.rb
87
+ - lib/redness/red_set_multi.rb
88
+ - lib/redness/red_set_union.rb
89
+ - lib/redness/timed_red_set.rb
90
+ - lib/version.rb
91
+ - redness.gemspec
92
+ - spec/precisionable.rb
93
+ - spec/redis_runner.rb
94
+ - spec/redness/red_capped_list_spec.rb
95
+ - spec/redness/red_expire_spec.rb
96
+ - spec/redness/red_hash_spec.rb
97
+ - spec/redness/red_json_spec.rb
98
+ - spec/redness/red_list_spec.rb
99
+ - spec/redness/red_set_multi_spec.rb
100
+ - spec/redness/red_set_spec.rb
101
+ - spec/redness/red_set_union_spec.rb
102
+ - spec/redness/timed_red_set_spec.rb
103
+ - spec/spec_integration_helper.rb
104
+ homepage: http://github.com/howaboutwe/redness
105
+ licenses:
106
+ - MIT
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ! '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 1.8.16
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: Helpful Ruby classes for improved Redis data structures
129
+ test_files: []