stash 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/LICENSE +20 -0
- data/README.markdown +92 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/lib/stash/class_methods.rb +47 -0
- data/lib/stash/hash.rb +61 -0
- data/lib/stash/list.rb +90 -0
- data/lib/stash/redis_adapter.rb +132 -0
- data/lib/stash/string.rb +16 -0
- data/lib/stash.rb +43 -0
- data/spec/hash_spec.rb +29 -0
- data/spec/list_spec.rb +65 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/string_spec.rb +9 -0
- metadata +133 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Tony Arcieri
|
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/README.markdown
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
Stash
|
2
|
+
=====
|
3
|
+
|
4
|
+
*"The sloping companion I cast down the ash, yanked on my tunic and dangled my stash"*
|
5
|
+
|
6
|
+
Stash provides Ruby-like classes for interacting with data structures servers.
|
7
|
+
Presently the only such server supported is Redis, however support is planned
|
8
|
+
for Membase, Memcache, and possibly Kestrel.
|
9
|
+
|
10
|
+
Initializing Stash
|
11
|
+
------------------
|
12
|
+
|
13
|
+
Stash supports multiple connections which are initialized through the
|
14
|
+
stash Stash.setup command. Stash operates primarily through the default
|
15
|
+
connection:
|
16
|
+
|
17
|
+
Stash.setup :default, :adapter => :redis, :host => 'localhost'
|
18
|
+
|
19
|
+
This will connect to the Redis server listening on localhost. Stash.setup
|
20
|
+
takes an options hash with the following parameters:
|
21
|
+
|
22
|
+
- **adapter**: must be "redis" (*mandatory*)
|
23
|
+
- **host**: hostname or IP address of the Redis server (*mandatory*)
|
24
|
+
- **port**: port of the Redis server (*optional, default 6379*)
|
25
|
+
- **namespace**: Redis namspace to use (*optional, default global namespace*)
|
26
|
+
- **password**: password to the Redis server (*optional*)
|
27
|
+
|
28
|
+
Strings
|
29
|
+
-------
|
30
|
+
|
31
|
+
The core type exposed by Stash is a string. You can store a string in the
|
32
|
+
default Stash using:
|
33
|
+
|
34
|
+
Stash[:foobar] = "mystring"
|
35
|
+
|
36
|
+
This string can be retrieved as easily as:
|
37
|
+
|
38
|
+
Stash[:foobar]
|
39
|
+
|
40
|
+
And deleted with:
|
41
|
+
|
42
|
+
Stash.delete :foobar
|
43
|
+
|
44
|
+
Lists
|
45
|
+
-----
|
46
|
+
|
47
|
+
Stash supports a mostly ordered list type. You can retrieve a list with:
|
48
|
+
|
49
|
+
Stash::List[:baz]
|
50
|
+
|
51
|
+
Stash Lists support most of the methods you'd expect on the Ruby Array type.
|
52
|
+
They are enumerable and support all of Ruby's Enumerable methods.
|
53
|
+
|
54
|
+
You can push elements onto the list with:
|
55
|
+
|
56
|
+
Stash::List[:baz] << 'asdf'
|
57
|
+
|
58
|
+
And pop them with:
|
59
|
+
|
60
|
+
Stash::List[:baz].pop
|
61
|
+
|
62
|
+
Lists can be converted to arrays with:
|
63
|
+
|
64
|
+
Stash::List[:baz].to_a
|
65
|
+
|
66
|
+
Or iterated with:
|
67
|
+
|
68
|
+
Stash::List[:baz].each { |elem| ... }
|
69
|
+
|
70
|
+
Stash asks you to think of lists as being somewhat loosely ordered. This
|
71
|
+
means that Stash will make its best effort to give you a list in order,
|
72
|
+
however that order may shift around depending on how the backend storage
|
73
|
+
system is implemented. Distributed systems may be interesting properties.
|
74
|
+
|
75
|
+
Hashes
|
76
|
+
------
|
77
|
+
|
78
|
+
Stash hashes work like Ruby hashes. You can retrieve a Stash::Hash with:
|
79
|
+
|
80
|
+
Stash::Hash[:qux]
|
81
|
+
|
82
|
+
You can set members of a Stash::Hash with:
|
83
|
+
|
84
|
+
Stash::Hash[:qux][:omgwtf] == "bbq"
|
85
|
+
|
86
|
+
or retrieve them with:
|
87
|
+
|
88
|
+
Stash::Hash[:qux][:omgwtf]
|
89
|
+
|
90
|
+
You can convert a Stash::Hash to a Ruby hash with:
|
91
|
+
|
92
|
+
Stash::Hash[:qux].to_hash
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "stash"
|
8
|
+
gem.summary = "Abstract interface to data structures servers"
|
9
|
+
gem.description = "Stash maps the facilities provided by data structures servers onto classes which mimic Ruby's built-in types"
|
10
|
+
gem.email = "tony@medioh.com"
|
11
|
+
gem.homepage = "http://github.com/tarcieri/stash"
|
12
|
+
gem.authors = ["Tony Arcieri"]
|
13
|
+
gem.add_dependency "redis", "~> 2.1.0"
|
14
|
+
gem.add_dependency "redis-namespace", "~> 0.10.0"
|
15
|
+
gem.add_development_dependency "rspec", "~> 2.1.0"
|
16
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
end
|
18
|
+
Jeweler::GemcutterTasks.new
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'rspec/core/rake_task'
|
24
|
+
RSpec::Core::RakeTask.new(:rspec) do |rspec|
|
25
|
+
rspec.rspec_opts = %w[-fs -c -b]
|
26
|
+
end
|
27
|
+
|
28
|
+
RSpec::Core::RakeTask.new(:rspec) do |rspec|
|
29
|
+
rspec.rspec_opts = %w[-fs -c -b]
|
30
|
+
rspec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :rspec => :check_dependencies
|
34
|
+
task :spec => :rspec
|
35
|
+
|
36
|
+
task :default => :rspec
|
37
|
+
|
38
|
+
require 'rake/rdoctask'
|
39
|
+
Rake::RDocTask.new do |rdoc|
|
40
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
43
|
+
rdoc.title = "stash #{version}"
|
44
|
+
rdoc.rdoc_files.include('README*')
|
45
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
46
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Stash
|
2
|
+
# Configuration error
|
3
|
+
class ConfigError < StandardError; end
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def [](key)
|
7
|
+
default[key]
|
8
|
+
end
|
9
|
+
|
10
|
+
def []=(key, value)
|
11
|
+
default[key] = value
|
12
|
+
end
|
13
|
+
|
14
|
+
# Add a configuration entry to Stash
|
15
|
+
def setup(label, configuration)
|
16
|
+
@configurations ||= {}
|
17
|
+
@configurations[label.to_sym] = configuration
|
18
|
+
|
19
|
+
if label == :default
|
20
|
+
@default = nil
|
21
|
+
default
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Retrieve the default Stash configuration
|
26
|
+
def default
|
27
|
+
@default ||= begin
|
28
|
+
raise ConfigError, "please configure me with Stash.setup" unless @configurations
|
29
|
+
raise ConfigError, "no default configuration" unless @configurations[:default]
|
30
|
+
new(@configurations[:default])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Retrieve a particular Stash configuration
|
35
|
+
def configuration(key)
|
36
|
+
@configurations[key.to_sym]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Delete a key from the Stash
|
40
|
+
def delete(key)
|
41
|
+
default.delete(key)
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
extend ClassMethods
|
47
|
+
end
|
data/lib/stash/hash.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# Stash::Hashes are remotely located lookup tables
|
2
|
+
class Stash::Hash
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def self.[](key)
|
6
|
+
new(key)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(key, adapter = Stash.default.adapter)
|
10
|
+
@name, @adapter = key.to_s, adapter
|
11
|
+
end
|
12
|
+
|
13
|
+
# Retrieve the value associated with a given key
|
14
|
+
def [](key)
|
15
|
+
@adapter.hash_get @name, key.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
# Set a value in the hash
|
19
|
+
def []=(key, value)
|
20
|
+
@adapter.hash_set @name, key.to_s, value.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
# Clear all elements from the hash
|
24
|
+
def clear
|
25
|
+
@adapter.delete @name
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
# Delete a value from the hash
|
30
|
+
def delete(key)
|
31
|
+
@adapter.hash_delete @name, key.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return the length of a hash
|
35
|
+
def length
|
36
|
+
@adapter.hash_length @name
|
37
|
+
end
|
38
|
+
|
39
|
+
# Is this hash empty?
|
40
|
+
def empty?
|
41
|
+
length == 0
|
42
|
+
end
|
43
|
+
|
44
|
+
# Cast to a Ruby hash
|
45
|
+
def to_hash
|
46
|
+
@adapter.hash_value @name
|
47
|
+
end
|
48
|
+
|
49
|
+
# Enumerate hashes
|
50
|
+
def each(&block)
|
51
|
+
to_hash.each(&block)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Inspect a hash
|
55
|
+
def inspect
|
56
|
+
"#<Stash::Hash[:#{@name}]: #{to_hash.inspect}>"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Create a string representation of a hash
|
60
|
+
alias_method :to_s, :inspect
|
61
|
+
end
|
data/lib/stash/list.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Stash::Lists are (mostly) ordered lists stored remotely on the server
|
2
|
+
class Stash::List
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def self.[](key)
|
6
|
+
new(key)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(key, adapter = Stash.default.adapter)
|
10
|
+
@key, @adapter = key.to_s, adapter
|
11
|
+
end
|
12
|
+
|
13
|
+
# Cast to an array
|
14
|
+
def to_ary
|
15
|
+
@adapter.list_range @key, 0, length
|
16
|
+
end
|
17
|
+
alias_method :to_a, :to_ary
|
18
|
+
|
19
|
+
# Get the length of a list
|
20
|
+
def length
|
21
|
+
@adapter.list_length(@key)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Is this list empty?
|
25
|
+
def empty?
|
26
|
+
length == 0
|
27
|
+
end
|
28
|
+
|
29
|
+
# Clear all elements from the list
|
30
|
+
def clear
|
31
|
+
@adapter.delete @key
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# Push an element onto the list (i.e. right push)
|
36
|
+
def push(elem)
|
37
|
+
@adapter.list_push @key, elem.to_s, :right
|
38
|
+
self
|
39
|
+
end
|
40
|
+
alias_method :<<, :push
|
41
|
+
alias_method :rpush, :push
|
42
|
+
|
43
|
+
# Unshift an element onto the list (i.e. left push)
|
44
|
+
def unshift(elem)
|
45
|
+
@adapter.list_push @key, elem.to_s, :left
|
46
|
+
end
|
47
|
+
alias_method :lpush, :unshift
|
48
|
+
|
49
|
+
# Pop from a list (i.e. right pop)
|
50
|
+
def pop
|
51
|
+
@adapter.list_pop @key, :right
|
52
|
+
end
|
53
|
+
alias_method :rpop, :pop
|
54
|
+
|
55
|
+
# Shift from a list (i.e. left pop)
|
56
|
+
def shift
|
57
|
+
@adapter.list_pop @key, :left
|
58
|
+
end
|
59
|
+
alias_method :lpop, :shift
|
60
|
+
|
61
|
+
# Blocking shift: wait for a message to be pushed (with an optional timeout)
|
62
|
+
def bshift(timeout = nil)
|
63
|
+
@adapter.list_blocking_pop @key, :left, timeout
|
64
|
+
end
|
65
|
+
alias_method :blpop, :bshift
|
66
|
+
|
67
|
+
# Blocking pop: wait for a message to be unshifted (with an optional timeout)
|
68
|
+
def bpop(timeout = nil)
|
69
|
+
@adapter.list_blocking_pop @key, :right, timeout
|
70
|
+
end
|
71
|
+
alias_method :brpop, :bpop
|
72
|
+
|
73
|
+
# Iterate the list
|
74
|
+
def each(&block)
|
75
|
+
to_ary.each(&block)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Obtain the last element in a list
|
79
|
+
def last
|
80
|
+
to_ary[-1]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Inspect a list
|
84
|
+
def inspect
|
85
|
+
"#<Stash::List[:#{@key}]: #{to_ary.inspect}>"
|
86
|
+
end
|
87
|
+
|
88
|
+
# Create a string representation of a list
|
89
|
+
alias_method :to_s, :inspect
|
90
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require 'redis-namespace'
|
3
|
+
|
4
|
+
class Stash
|
5
|
+
# Adapter for the Redis data structures server.
|
6
|
+
# See http://code.google.com/p/redis/
|
7
|
+
class RedisAdapter
|
8
|
+
attr_reader :capabilities
|
9
|
+
|
10
|
+
def initialize(config)
|
11
|
+
# Symbolize keys in config
|
12
|
+
config = config.inject({}) { |h, (k, v)| h[k.to_sym] = v; h }
|
13
|
+
|
14
|
+
raise ArgumentError, "missing 'host' key" unless config[:host]
|
15
|
+
config[:port] ||= 6379 # Default Redis port
|
16
|
+
|
17
|
+
redis = Redis.new config
|
18
|
+
redis = Redis::Namespace.new config[:namespace], :redis => redis if config[:namespace]
|
19
|
+
|
20
|
+
@capabilities = [:string, :list, :hash]
|
21
|
+
|
22
|
+
# Redis 2.0RC+ supports blocking pop
|
23
|
+
@capabilities << :bpop if redis.info['redis_version'] >= "1.3.0"
|
24
|
+
|
25
|
+
@redis = redis
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set a given key within Redis
|
29
|
+
def []=(key, value)
|
30
|
+
@redis.set key.to_s, value.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
# Retrieve a given key from Redis
|
34
|
+
def [](key)
|
35
|
+
case type(key)
|
36
|
+
when "none" then nil
|
37
|
+
when "string" then Stash::String.new key, self
|
38
|
+
when "list" then Stash::List.new key, self
|
39
|
+
else raise "unknown Redis key type: #{key}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Retrieve the type for a given key
|
44
|
+
def type(key)
|
45
|
+
@redis.type key.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
# Retrieve a key as a string
|
49
|
+
def get(key)
|
50
|
+
@redis.get key.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
# Delete a key
|
54
|
+
def delete(key)
|
55
|
+
@redis.del key.to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
# Push an element onto a list
|
59
|
+
def list_push(name, value, side)
|
60
|
+
case side
|
61
|
+
when :right
|
62
|
+
@redis.rpush name.to_s, value.to_s
|
63
|
+
when :left
|
64
|
+
@redis.lpush name.to_s, value.to_s
|
65
|
+
else raise ArgumentError, "left or right plztks"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Pop from a list
|
70
|
+
def list_pop(name, side)
|
71
|
+
case side
|
72
|
+
when :right
|
73
|
+
@redis.rpop name.to_s
|
74
|
+
when :left
|
75
|
+
@redis.lpop name.to_s
|
76
|
+
else raise ArgumentError, "left or right plztks"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Blocking pop from a list
|
81
|
+
def list_blocking_pop(name, side, timeout = nil)
|
82
|
+
timeout ||= 0
|
83
|
+
res = case side
|
84
|
+
when :left
|
85
|
+
@redis.blpop name, timeout
|
86
|
+
when :right
|
87
|
+
@redis.brpop name, timeout
|
88
|
+
else raise ArgumentError, "left or right plztks"
|
89
|
+
end
|
90
|
+
|
91
|
+
return res[1] if res
|
92
|
+
raise Stash::TimeoutError, "request timed out"
|
93
|
+
end
|
94
|
+
|
95
|
+
# Retrieve the length of a list
|
96
|
+
def list_length(name)
|
97
|
+
@redis.llen name.to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
# Retrieve the given range from a list
|
101
|
+
def list_range(name, from, to)
|
102
|
+
@redis.lrange name.to_s, from, to
|
103
|
+
end
|
104
|
+
|
105
|
+
# Retrieve a value from a hash
|
106
|
+
def hash_get(name, key)
|
107
|
+
res = @redis.hget name.to_s, key.to_s
|
108
|
+
return if res == ""
|
109
|
+
res
|
110
|
+
end
|
111
|
+
|
112
|
+
# Store a value in a hash
|
113
|
+
def hash_set(name, key, value)
|
114
|
+
@redis.hset name.to_s, key.to_s, value.to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
# Retrieve the contents of a hash as a Ruby hash
|
118
|
+
def hash_value(name)
|
119
|
+
@redis.hgetall name.to_s
|
120
|
+
end
|
121
|
+
|
122
|
+
# Delete an entry from a hash
|
123
|
+
def hash_delete(name, key)
|
124
|
+
@redis.hdel name.to_s, key.to_s
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return the length of a hash
|
128
|
+
def hash_length(name)
|
129
|
+
@redis.hlen name.to_s
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/stash/string.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Strings are Stash's most basic type, and the only non-collection type
|
2
|
+
# They store exactly that, a string, and nothing more
|
3
|
+
class Stash::String
|
4
|
+
def initialize(key, adapter = Stash.default.adapter)
|
5
|
+
@adapter, @key = adapter, key.to_s
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_string
|
9
|
+
@adapter.get @key
|
10
|
+
end
|
11
|
+
alias_method :to_s, :to_string
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"#<Stash::String[:#{key}]: #{to_s}>"
|
15
|
+
end
|
16
|
+
end
|
data/lib/stash.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Stash is an abstract interface to data structures servers
|
2
|
+
class Stash
|
3
|
+
attr_reader :adapter
|
4
|
+
|
5
|
+
# Timeout when performing a blocking action, such as blocking pop
|
6
|
+
class TimeoutError < StandardError; end
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
raise ArgumentError, "no adapter specified" unless config[:adapter]
|
10
|
+
adapter_name = config[:adapter].to_s.split('_').map { |s| s.capitalize }.join
|
11
|
+
adapter_name += "Adapter"
|
12
|
+
|
13
|
+
begin
|
14
|
+
adapter_class = Stash.const_get adapter_name
|
15
|
+
rescue NameError
|
16
|
+
raise ArgumentError, "unknown adapter: #{config[:adapter]}"
|
17
|
+
end
|
18
|
+
|
19
|
+
@adapter = adapter_class.new config
|
20
|
+
end
|
21
|
+
|
22
|
+
# Store an object in the Stash
|
23
|
+
def []=(key, value)
|
24
|
+
@adapter[key.to_s] = value
|
25
|
+
end
|
26
|
+
|
27
|
+
# Retrieve an object from the Stash
|
28
|
+
def [](key)
|
29
|
+
@adapter[key.to_s]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Remove a key from the Stash
|
33
|
+
def delete(key)
|
34
|
+
@adapter.delete(key.to_s)
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'stash/class_methods'
|
40
|
+
require 'stash/redis_adapter'
|
41
|
+
require 'stash/string'
|
42
|
+
require 'stash/list'
|
43
|
+
require 'stash/hash'
|
data/spec/hash_spec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Stash::Hash do
|
4
|
+
before :each do
|
5
|
+
@hash = Stash::Hash[:foobar]
|
6
|
+
@hash.clear
|
7
|
+
end
|
8
|
+
|
9
|
+
it "clears hashes" do
|
10
|
+
@hash[:foo] = "a"
|
11
|
+
@hash[:bar] = "b"
|
12
|
+
@hash.clear
|
13
|
+
@hash.to_hash.should == {}
|
14
|
+
end
|
15
|
+
|
16
|
+
it "sets and gets strings from hashes" do
|
17
|
+
@hash[:foo].should be_nil
|
18
|
+
@hash[:foo] = "42"
|
19
|
+
@hash[:foo].should == "42"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "casts to a Ruby hash" do
|
23
|
+
@hash.should be_empty
|
24
|
+
@hash['foo'] = 'a'
|
25
|
+
@hash['bar'] = 'b'
|
26
|
+
|
27
|
+
@hash.to_hash.should == {'foo'=>'a', 'bar'=>'b'}
|
28
|
+
end
|
29
|
+
end
|
data/spec/list_spec.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Stash::List do
|
4
|
+
before :each do
|
5
|
+
@list = Stash::List[:foobar]
|
6
|
+
@list.clear
|
7
|
+
end
|
8
|
+
|
9
|
+
it "clears lists" do
|
10
|
+
@list << "x"
|
11
|
+
@list << "y"
|
12
|
+
@list << "z"
|
13
|
+
@list.clear
|
14
|
+
@list.to_a.should == []
|
15
|
+
end
|
16
|
+
|
17
|
+
it "pushes to lists" do
|
18
|
+
# Normal syntax
|
19
|
+
@list.push "zomg"
|
20
|
+
|
21
|
+
# Cool syntax
|
22
|
+
@list << "wtf"
|
23
|
+
|
24
|
+
@list.to_a.should == ["zomg", "wtf"]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "unshifts to lists" do
|
28
|
+
@list.unshift "omfg"
|
29
|
+
@list.unshift "lulz"
|
30
|
+
|
31
|
+
@list.to_a.should == ["lulz", "omfg"]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "pops from lists" do
|
35
|
+
@list << "x"
|
36
|
+
@list << "y"
|
37
|
+
@list << "z"
|
38
|
+
@list.pop.should == "z"
|
39
|
+
@list.to_a.should == ["x", "y"]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "shifts from lists" do
|
43
|
+
@list << "x"
|
44
|
+
@list << "y"
|
45
|
+
@list << "z"
|
46
|
+
@list.shift.should == "x"
|
47
|
+
@list.to_a.should == ["y", "z"]
|
48
|
+
end
|
49
|
+
|
50
|
+
it "knows its length" do
|
51
|
+
@list.length.should == 0
|
52
|
+
|
53
|
+
@list << "x"
|
54
|
+
@list << "y"
|
55
|
+
@list << "z"
|
56
|
+
@list.length.should == 3
|
57
|
+
end
|
58
|
+
|
59
|
+
it "knows if it's empty" do
|
60
|
+
@list.should be_empty
|
61
|
+
|
62
|
+
@list << "something"
|
63
|
+
@list.should_not be_empty
|
64
|
+
end
|
65
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
|
4
|
+
require 'stash'
|
5
|
+
require 'rspec'
|
6
|
+
require 'rspec/autorun'
|
7
|
+
|
8
|
+
# Connect to Redis
|
9
|
+
Stash.setup :default, :adapter => :redis,
|
10
|
+
:namespace => 'stash_test',
|
11
|
+
:host => '127.0.0.1',
|
12
|
+
:port => 6379
|
data/spec/string_spec.rb
ADDED
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Tony Arcieri
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-30 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: redis
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 11
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 1
|
33
|
+
- 0
|
34
|
+
version: 2.1.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: redis-namespace
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 55
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 10
|
49
|
+
- 0
|
50
|
+
version: 0.10.0
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rspec
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 11
|
62
|
+
segments:
|
63
|
+
- 2
|
64
|
+
- 1
|
65
|
+
- 0
|
66
|
+
version: 2.1.0
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id003
|
69
|
+
description: Stash maps the facilities provided by data structures servers onto classes which mimic Ruby's built-in types
|
70
|
+
email: tony@medioh.com
|
71
|
+
executables: []
|
72
|
+
|
73
|
+
extensions: []
|
74
|
+
|
75
|
+
extra_rdoc_files:
|
76
|
+
- LICENSE
|
77
|
+
- README.markdown
|
78
|
+
files:
|
79
|
+
- .document
|
80
|
+
- LICENSE
|
81
|
+
- README.markdown
|
82
|
+
- Rakefile
|
83
|
+
- VERSION
|
84
|
+
- lib/stash.rb
|
85
|
+
- lib/stash/class_methods.rb
|
86
|
+
- lib/stash/hash.rb
|
87
|
+
- lib/stash/list.rb
|
88
|
+
- lib/stash/redis_adapter.rb
|
89
|
+
- lib/stash/string.rb
|
90
|
+
- spec/hash_spec.rb
|
91
|
+
- spec/list_spec.rb
|
92
|
+
- spec/spec.opts
|
93
|
+
- spec/spec_helper.rb
|
94
|
+
- spec/string_spec.rb
|
95
|
+
has_rdoc: true
|
96
|
+
homepage: http://github.com/tarcieri/stash
|
97
|
+
licenses: []
|
98
|
+
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
hash: 3
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
version: "0"
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
hash: 3
|
119
|
+
segments:
|
120
|
+
- 0
|
121
|
+
version: "0"
|
122
|
+
requirements: []
|
123
|
+
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 1.3.7
|
126
|
+
signing_key:
|
127
|
+
specification_version: 3
|
128
|
+
summary: Abstract interface to data structures servers
|
129
|
+
test_files:
|
130
|
+
- spec/hash_spec.rb
|
131
|
+
- spec/list_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
- spec/string_spec.rb
|