blombo 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/Manifest +7 -0
- data/README +95 -0
- data/Rakefile +9 -0
- data/blombo.gemspec +33 -0
- data/lib/blombo.rb +129 -0
- data/test/test_all.rb +101 -0
- data/test/test_helper.rb +4 -0
- metadata +73 -0
data/CHANGELOG
ADDED
data/Manifest
ADDED
data/README
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
Blombo
|
3
|
+
-------
|
4
|
+
|
5
|
+
Tread redis-server like a deep ruby hash.
|
6
|
+
|
7
|
+
|
8
|
+
Example usage
|
9
|
+
-------------
|
10
|
+
|
11
|
+
Blombo.redis = Redis.new
|
12
|
+
$blombo = Blombo.new('ServerApp')
|
13
|
+
|
14
|
+
$blombo.servers.status['web1'] = 'ok'
|
15
|
+
$blombo.servers.status['web2'] = 'down'
|
16
|
+
|
17
|
+
$blombo.servers.status[:web1]
|
18
|
+
#=> "ok"
|
19
|
+
|
20
|
+
$blombo.servers.status[:web3]
|
21
|
+
#=> nil
|
22
|
+
|
23
|
+
This creates a Redis Hash with the key 'blombo:ServerApp:servers:status',
|
24
|
+
and two fields ('web1', 'web2'), with two associated values.
|
25
|
+
|
26
|
+
You can store any Ruby objects as values, they will be automaticallly
|
27
|
+
Marshalled. (Strings are stored as-is). Blombo does not cache anything but
|
28
|
+
goes back to redis to get data whenever it is requested with a lookup[key].
|
29
|
+
|
30
|
+
Ruby's Enumerable is included along with each() so you can enjoy the usual
|
31
|
+
range of ruby hash methods:
|
32
|
+
|
33
|
+
$blombo.server.status
|
34
|
+
#=> #<Blombo:0x9ab0d84 @name="ServerApp:server:status" ...>
|
35
|
+
|
36
|
+
$blombo.servers.status.exists
|
37
|
+
=> true
|
38
|
+
|
39
|
+
$blombo.servers.status.keys
|
40
|
+
#=> ['web1', 'web2']
|
41
|
+
|
42
|
+
$blombo.servers.status.type
|
43
|
+
#=> 'hash'
|
44
|
+
|
45
|
+
$blombo.servers.status.select {|server, status| status == 'ok' }
|
46
|
+
#=> [["web1", "ok"]]
|
47
|
+
|
48
|
+
|
49
|
+
Blombo only saves the object back into redis if the []= method is used.
|
50
|
+
Assignment must always be used to save to db. This means you should
|
51
|
+
maintain ruby objects like arrays carefully:
|
52
|
+
|
53
|
+
$blombo.servers[:list] = ["web1"]
|
54
|
+
$blombo.servers[:list] << "web2"
|
55
|
+
$blombo.servers[:list]
|
56
|
+
#=> ["web1"] #oops
|
57
|
+
|
58
|
+
$blombo.servers[:list] += ["web2"]
|
59
|
+
$blombo.servers[:list]
|
60
|
+
#=> ["web1", "web2"] #ahh!
|
61
|
+
|
62
|
+
|
63
|
+
Other Redis types
|
64
|
+
-----------------
|
65
|
+
|
66
|
+
What if I don't want a Redis hash? Thats OK - Blombo passes through redis
|
67
|
+
commands curried with the key as the first parameter:
|
68
|
+
|
69
|
+
This: $blombo.joblist.rpush('job1')
|
70
|
+
Equals: $redis.rpush('blombo:ServerApp:joblist', 'job1')
|
71
|
+
|
72
|
+
Then I can pop it off the list:
|
73
|
+
|
74
|
+
$blombo.joblist.type
|
75
|
+
#=> "list"
|
76
|
+
|
77
|
+
$blombo.joblist.llen
|
78
|
+
#=> 1
|
79
|
+
|
80
|
+
$blombo.joblist.lpop
|
81
|
+
#=> "job1"
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
Contact the author
|
89
|
+
------------------
|
90
|
+
|
91
|
+
Andrew Snow <andrew@modulus.org>
|
92
|
+
Andys^ on irc.freenode.net
|
93
|
+
|
94
|
+
|
95
|
+
Blombo never forgets.
|
data/Rakefile
ADDED
data/blombo.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "blombo"
|
5
|
+
s.version = "0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Andrew Snow"]
|
9
|
+
s.date = "2012-02-10"
|
10
|
+
s.description = "Blombo: Treat redis-server like a deep ruby hash"
|
11
|
+
s.email = "andrew@modulus.org"
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "README", "lib/blombo.rb"]
|
13
|
+
s.files = ["CHANGELOG", "README", "Rakefile", "lib/blombo.rb", "test/test_all.rb", "test/test_helper.rb", "Manifest", "blombo.gemspec"]
|
14
|
+
s.homepage = "http://github.com/andys/blombo"
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Blombo", "--main", "README"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = "blombo"
|
18
|
+
s.rubygems_version = "1.8.10"
|
19
|
+
s.summary = "Blombo: Treat redis-server like a deep ruby hash"
|
20
|
+
s.test_files = ["test/test_all.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
|
+
s.add_runtime_dependency(%q<redis>, [">= 0"])
|
27
|
+
else
|
28
|
+
s.add_dependency(%q<redis>, [">= 0"])
|
29
|
+
end
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<redis>, [">= 0"])
|
32
|
+
end
|
33
|
+
end
|
data/lib/blombo.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
|
2
|
+
class Blombo
|
3
|
+
|
4
|
+
include Enumerable
|
5
|
+
include Comparable
|
6
|
+
attr_reader :blombo_parent
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :redis
|
10
|
+
def is_marshalled?(str)
|
11
|
+
Marshal.dump(nil)[0,2] == str[0,2] # Marshall stores its version info in first 2 bytes
|
12
|
+
end
|
13
|
+
def to_redis_val(obj)
|
14
|
+
if Integer===obj || String===obj && obj !~ /^\d+$/ && !is_marshalled?(obj)
|
15
|
+
obj.to_s
|
16
|
+
else
|
17
|
+
Marshal.dump(obj)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
def from_redis_val(str)
|
21
|
+
if(is_marshalled?(str))
|
22
|
+
Marshal.load(str)
|
23
|
+
elsif(str =~ /^\d+$/)
|
24
|
+
str.to_i
|
25
|
+
else
|
26
|
+
str
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def redis
|
32
|
+
self.class.redis
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(name, blombo_parent=nil)
|
36
|
+
@name = name
|
37
|
+
@blombo_parent = blombo_parent
|
38
|
+
end
|
39
|
+
|
40
|
+
def <=>(other)
|
41
|
+
@name <=> other.blombo_name
|
42
|
+
end
|
43
|
+
|
44
|
+
def blombo_key
|
45
|
+
"blombo:#{@name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def blombo_name
|
49
|
+
@name
|
50
|
+
end
|
51
|
+
|
52
|
+
def []=(key, val)
|
53
|
+
redis.hset(blombo_key, key.to_s, self.class.to_redis_val(val))
|
54
|
+
end
|
55
|
+
|
56
|
+
def defined?(key)
|
57
|
+
redis.exists(key.to_s)
|
58
|
+
end
|
59
|
+
|
60
|
+
def [](key)
|
61
|
+
if(val = redis.hget(blombo_key, key.to_s))
|
62
|
+
self.class.from_redis_val(val)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def nil?
|
67
|
+
empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
def empty?
|
71
|
+
redis.hlen(blombo_key) == 0
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_hash
|
75
|
+
redis.hgetall(blombo_key)
|
76
|
+
end
|
77
|
+
|
78
|
+
def each(*args, &bl)
|
79
|
+
to_hash.each(*args, &bl)
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_a
|
83
|
+
redis.hgetall(blombo_key).to_a
|
84
|
+
end
|
85
|
+
|
86
|
+
def keys
|
87
|
+
self.class.redis.hkeys(blombo_key)
|
88
|
+
end
|
89
|
+
|
90
|
+
def values
|
91
|
+
self.class.redis.hvals(blombo_key).map {|v| self.class.from_redis_val(v) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def type
|
95
|
+
@type ||= (t = Blombo.redis.type(blombo_key)) && t != 'none' && t || nil
|
96
|
+
end
|
97
|
+
|
98
|
+
def method_missing(meth, *params, &bl)
|
99
|
+
if Blombo.redis.respond_to?(meth)
|
100
|
+
Blombo.redis.send(meth, blombo_key, *params, &bl)
|
101
|
+
elsif params.empty? && meth =~ /^[a-z_][a-z0-9_]*$/i
|
102
|
+
Blombo.new("#{@name}:#{meth}", self)
|
103
|
+
else
|
104
|
+
super(meth, *params, &bl)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def blombo_children
|
109
|
+
self.class.redis.keys("#{blombo_key}:*").map {|k| Blombo.new(k.gsub(/^blombo:/,''), self) }
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
=begin
|
116
|
+
|
117
|
+
blombo = Blombo.new(redis server details)
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
blombo.blah = {'hello' => 'world'}
|
122
|
+
blombo.blah = OpenStruct(..)
|
123
|
+
blombo.blah = activerecord model
|
124
|
+
|
125
|
+
May as well assign each object a uniq id (using redis counters?)
|
126
|
+
to allow referencing / assocations
|
127
|
+
|
128
|
+
|
129
|
+
=end
|
data/test/test_all.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/../lib/blombo"
|
2
|
+
require 'test/unit'
|
3
|
+
require 'redis'
|
4
|
+
require "#{File.dirname(__FILE__)}/test_helper"
|
5
|
+
|
6
|
+
class TestBlombo < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
$redis.flushdb
|
9
|
+
Blombo.redis = $redis
|
10
|
+
@blombo = Blombo.new('test')
|
11
|
+
@blombo[:flibble] = "test123"
|
12
|
+
@blombo['derp'] = 'test321'
|
13
|
+
@blombo.deep[:firstname] = 'Herp'
|
14
|
+
@blombo.deep[:lastname] = 'Derpington'
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_defined
|
18
|
+
assert_equal false, @blombo.defined?('foo')
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_undefined_redis_type
|
22
|
+
assert_nil @blombo.foo.type
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_empty_hash_lookup
|
26
|
+
assert_nil @blombo['foo']
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_hash_type
|
30
|
+
assert_equal 'hash', @blombo.type
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_hash_setter
|
34
|
+
assert_equal({'flibble' => 'test123', 'derp' => 'test321'}, $redis.hgetall('blombo:test'))
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_values
|
38
|
+
assert_equal(['test123', 'test321'], @blombo.values.sort)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_keys
|
42
|
+
assert_equal(['derp', 'flibble'], @blombo.keys.sort)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_each
|
46
|
+
# Blombo includes Enumerable so we can test #each with #inject
|
47
|
+
result = @blombo.inject({}) {|hsh, keyval| hsh.merge!(keyval.first => keyval.last) }
|
48
|
+
assert_equal($redis.hgetall('blombo:test'), result)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_deep_empty_type
|
52
|
+
assert Blombo===@blombo.deep
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_deep_empty_array
|
56
|
+
assert_equal [], @blombo.empty.to_a
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_deep_empty_method
|
60
|
+
assert_equal true, @blombo.empty.empty?
|
61
|
+
assert_equal false, @blombo.deep.empty?
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_deep_empty_hash_lookup
|
65
|
+
assert_nil @blombo.deep['foo']
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_redis_keys
|
69
|
+
assert_equal(['blombo:test', 'blombo:test:deep'], $redis.keys.sort)
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_deep_hash_setter
|
73
|
+
assert_equal({'firstname' => 'Herp', 'lastname' => 'Derpington'}, $redis.hgetall('blombo:test:deep'))
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_deeper_hash
|
77
|
+
@blombo.a.b.c['d'] = 'e'
|
78
|
+
assert_equal({'d' => 'e'}, $redis.hgetall('blombo:test:a:b:c'))
|
79
|
+
assert_equal [@blombo.a.b.c], @blombo.a.blombo_children
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_marshal
|
83
|
+
@blombo.marshaltest[:number] = 12345
|
84
|
+
@blombo.marshaltest[:nil] = nil
|
85
|
+
assert_equal 12345, @blombo.marshaltest[:number]
|
86
|
+
assert_equal nil, @blombo.marshaltest[:nil]
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_redis_ops
|
90
|
+
@blombo.listy.rpush('job1')
|
91
|
+
@blombo.listy.rpush('job2')
|
92
|
+
assert_equal 2, $redis.llen('blombo:test:listy')
|
93
|
+
assert_equal ['job1', 'job2'], @blombo.listy.lrange(0, -1)
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_redis_list_type
|
97
|
+
@blombo.listy.rpush('job1')
|
98
|
+
assert_equal 'list', @blombo.listy.type
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: blombo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Andrew Snow
|
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: &82662180 !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: *82662180
|
25
|
+
description: ! 'Blombo: Treat redis-server like a deep ruby hash'
|
26
|
+
email: andrew@modulus.org
|
27
|
+
executables: []
|
28
|
+
extensions: []
|
29
|
+
extra_rdoc_files:
|
30
|
+
- CHANGELOG
|
31
|
+
- README
|
32
|
+
- lib/blombo.rb
|
33
|
+
files:
|
34
|
+
- CHANGELOG
|
35
|
+
- README
|
36
|
+
- Rakefile
|
37
|
+
- lib/blombo.rb
|
38
|
+
- test/test_all.rb
|
39
|
+
- test/test_helper.rb
|
40
|
+
- Manifest
|
41
|
+
- blombo.gemspec
|
42
|
+
homepage: http://github.com/andys/blombo
|
43
|
+
licenses: []
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --line-numbers
|
47
|
+
- --inline-source
|
48
|
+
- --title
|
49
|
+
- Blombo
|
50
|
+
- --main
|
51
|
+
- README
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '1.2'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project: blombo
|
68
|
+
rubygems_version: 1.8.10
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: ! 'Blombo: Treat redis-server like a deep ruby hash'
|
72
|
+
test_files:
|
73
|
+
- test/test_all.rb
|