stash 0.1.0

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
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
@@ -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
@@ -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
@@ -0,0 +1,9 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Stash::String do
4
+ it "stores strings" do
5
+ Stash[:foobar] = "baz"
6
+ Stash[:foobar].should be_an_instance_of(Stash::String)
7
+ Stash[:foobar].to_s.should == "baz"
8
+ end
9
+ end
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