stash 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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