redis_selector 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 +4 -0
- data/Gemfile +4 -0
- data/README.md +79 -0
- data/Rakefile +1 -0
- data/lib/redis_selector/.rb +5 -0
- data/lib/redis_selector/version.rb +3 -0
- data/lib/redis_selector.rb +58 -0
- data/redis_selector/.gemspec +20 -0
- data/redis_selector.gemspec +24 -0
- metadata +76 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# MockRedis
|
2
|
+
|
3
|
+
A gem to make it easy to use different Redises for different things.
|
4
|
+
|
5
|
+
## Getting Started
|
6
|
+
|
7
|
+
RedisSelector is a module that provides the `#with_redis` method.
|
8
|
+
|
9
|
+
`#with_redis` takes a string and a block, and calls the block with a
|
10
|
+
connected Redis object. The connection is closed after the block
|
11
|
+
exits.
|
12
|
+
|
13
|
+
The string is used to indicate what logical thing you're using Redis
|
14
|
+
for. This allows for easy addition of new Redis servers if your data
|
15
|
+
set starts getting too big.
|
16
|
+
|
17
|
+
Example:
|
18
|
+
|
19
|
+
class Competitor < ActiveRecord::Base
|
20
|
+
extend MockRedis
|
21
|
+
|
22
|
+
def self.top10
|
23
|
+
with_redis('leaderboards') do |redis|
|
24
|
+
redis.zrange(LEADERBOARD_KEY, 0, 9).map {|id| self.find(id)}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class BlogPostsController < ApplicationController
|
30
|
+
|
31
|
+
def create
|
32
|
+
# ...
|
33
|
+
with_redis('blog_posts') {|r| r.incr('blog_posts_created' }
|
34
|
+
render
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
By default, both these calls will connect to the same Redis. Now,
|
40
|
+
let's say your leaderboard data is getting really big, and it's taking
|
41
|
+
up too much space on your one little Redis machine. You spin up a new
|
42
|
+
one and tell `RedisSelector` about it:
|
43
|
+
|
44
|
+
RedisSelector.configure(
|
45
|
+
'leaderboards' => {'host' => 'big-huge-redis-box'}
|
46
|
+
# default is localhost for things not mentioned here
|
47
|
+
)
|
48
|
+
|
49
|
+
Then, Marketing goes crazy and starts posting blogs all over the
|
50
|
+
place, and all your blog posts start taking up a ton of space.
|
51
|
+
|
52
|
+
RedisSelector.configure(
|
53
|
+
'leaderboards' => {'host' => 'big-huge-redis-box'}
|
54
|
+
'blog_posts' => {'host' => 'other-redis-box'}
|
55
|
+
# default is localhost for things not mentioned here
|
56
|
+
)
|
57
|
+
|
58
|
+
That's the only code change you have to make. Throw that in
|
59
|
+
`config/environments/production.rb` and you're done. In development,
|
60
|
+
you probably haven't bothered to set anything up, so everything will
|
61
|
+
just go to localhost.
|
62
|
+
|
63
|
+
You can get separation by using different Redis databases, too. For
|
64
|
+
example, if two different models use the same Redis keys and step on
|
65
|
+
each other, you can split them up like so:
|
66
|
+
|
67
|
+
RedisSelector.configure(
|
68
|
+
'model1' => {'host' => 'redis-box', 'db' => 1}
|
69
|
+
'model2' => {'host' => 'redis-box', 'db' => 2}
|
70
|
+
# default is localhost/0 for things not mentioned here
|
71
|
+
)
|
72
|
+
|
73
|
+
Since they're in different databases, they get their own keyspaces and
|
74
|
+
don't step on each other.
|
75
|
+
|
76
|
+
## Testing
|
77
|
+
|
78
|
+
`RedisSelector` supports the use of `MockRedis` in test mode. Add
|
79
|
+
`mock_redis` to your `Gemfile`, and call `RedisSelector.mock!`.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "redis_selector/version"
|
2
|
+
|
3
|
+
module RedisSelector
|
4
|
+
DEFAULT_REDIS = {
|
5
|
+
'host' => 'localhost',
|
6
|
+
}.freeze
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :mocking
|
10
|
+
attr_accessor :mock_redises
|
11
|
+
attr_accessor :config
|
12
|
+
end
|
13
|
+
self.mock_redises ||= {}
|
14
|
+
|
15
|
+
def self.mock!
|
16
|
+
require 'mock_redis'
|
17
|
+
self.mocking = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.unmock!
|
21
|
+
self.mocking = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.configure(config)
|
25
|
+
self.config = config
|
26
|
+
end
|
27
|
+
|
28
|
+
# Rather than have one big global Redis, we let you get a Redis for
|
29
|
+
# a specific purpose. That way, if thing X's data set gets too
|
30
|
+
# large, you can point Redis.for(X) at a different redis-server
|
31
|
+
# instance, thus allowing for sharding based on logical data
|
32
|
+
# grouping.
|
33
|
+
#
|
34
|
+
# This doesn't address the problem of data migration.
|
35
|
+
def with_redis(what)
|
36
|
+
redis_selector = Module.nesting[0]
|
37
|
+
|
38
|
+
config = redis_selector.config || {}
|
39
|
+
redis_info = config[what] || config['default'] || DEFAULT_REDIS
|
40
|
+
|
41
|
+
redis = if redis_selector.mocking
|
42
|
+
# A MockRedis instance doesn't persist anywhere once we
|
43
|
+
# drop a reference to it, while a real Redis
|
44
|
+
# does. That's why we hold onto this mock like this;
|
45
|
+
# otherwise, repeated calls to with_redis(:foo) each get
|
46
|
+
# a completely empty mock.
|
47
|
+
redis_selector.mock_redises[redis_info['host']] ||= MockRedis.new
|
48
|
+
else
|
49
|
+
Redis.new(:host => redis_info['host'])
|
50
|
+
end
|
51
|
+
|
52
|
+
redis.select(redis_info['db']) if redis_info['db']
|
53
|
+
result = yield redis
|
54
|
+
result
|
55
|
+
ensure
|
56
|
+
redis.quit if redis
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "redis_selector//version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "redis_selector/"
|
7
|
+
s.version = RedisSelector/::VERSION
|
8
|
+
s.authors = ["Samuel Merritt"]
|
9
|
+
s.email = ["spam@andcheese.org"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{TODO: Write a gem summary}
|
12
|
+
s.description = %q{TODO: Write a gem description}
|
13
|
+
|
14
|
+
s.rubyforge_project = "redis_selector/"
|
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
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "redis_selector/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "redis_selector"
|
7
|
+
s.version = RedisSelector::VERSION
|
8
|
+
s.authors = ["Samuel Merritt"]
|
9
|
+
s.email = ["spam@andcheese.org"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Easy way to select different Redis instances/databases based on purpose.}
|
12
|
+
s.description = %q{Lets you select different Redis instances/databases easily.
|
13
|
+
This way, you get logical grouping of different Redis datasets.
|
14
|
+
|
15
|
+
Also supports mocking in test mode (via mock_redis gem).
|
16
|
+
}
|
17
|
+
|
18
|
+
s.rubyforge_project = "redis_selector"
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redis_selector
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Samuel Merritt
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-09-08 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: "Lets you select different Redis instances/databases easily.\n This way, you get logical grouping of different Redis datasets.\n\n Also supports mocking in test mode (via mock_redis gem).\n "
|
23
|
+
email:
|
24
|
+
- spam@andcheese.org
|
25
|
+
executables: []
|
26
|
+
|
27
|
+
extensions: []
|
28
|
+
|
29
|
+
extra_rdoc_files: []
|
30
|
+
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- Gemfile
|
34
|
+
- README.md
|
35
|
+
- Rakefile
|
36
|
+
- lib/redis_selector.rb
|
37
|
+
- lib/redis_selector/.rb
|
38
|
+
- lib/redis_selector/version.rb
|
39
|
+
- redis_selector.gemspec
|
40
|
+
- redis_selector/.gemspec
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: ""
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project: redis_selector
|
71
|
+
rubygems_version: 1.3.7
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Easy way to select different Redis instances/databases based on purpose.
|
75
|
+
test_files: []
|
76
|
+
|