redness 0.0.1

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.
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: []