memodis 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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ test/config
23
+ test/log
24
+ test/pid
25
+ test/dump.rdb
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "vendor/levicook/redis-rb"]
2
+ path = vendor/levicook/redis-rb
3
+ url = git@github.com:levicook/redis-rb.git
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 levicook@gmail.com
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.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ = memodis
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 levicook@gmail.com. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,146 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "memodis"
8
+ gem.summary = %Q{redis backed memoization helpers}
9
+ gem.description = %Q{
10
+ semi-transparent memoization; backed by redis; redis-rb and redis-namespace
11
+
12
+ Background
13
+ -----------
14
+ 1) http://blog.grayproductions.net/articles/caching_and_memoization
15
+ 2) http://code.google.com/p/redis & http://github.com/ezmobius/redis-rb
16
+
17
+ Important Moving Parts
18
+ ----------------------
19
+ 1) http://code.google.com/p/redis/wiki/GetCommand
20
+ 2) http://code.google.com/p/redis/wiki/SetCommand
21
+ 3) http://code.google.com/p/redis/wiki/SetnxCommand
22
+ 4) http://github.com/defunkt/redis-namespace
23
+ }
24
+ gem.email = "levicook@gmail.com"
25
+ gem.homepage = "http://github.com/levicook/memodis"
26
+ gem.authors = ["levicook@gmail.com"]
27
+ gem.add_development_dependency "riot", ">= 0"
28
+ gem.add_development_dependency "reek", ">= 0"
29
+ gem.add_development_dependency "daemon_controller", ">= 0"
30
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
31
+ end
32
+ Jeweler::GemcutterTasks.new
33
+ rescue LoadError
34
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
35
+ end
36
+
37
+ require 'rake/testtask'
38
+ Rake::TestTask.new(:test) do |test|
39
+ test.libs << 'lib' << 'test'
40
+ test.pattern = 'test/**/*_test.rb'
41
+ test.verbose = true
42
+ end
43
+
44
+ begin
45
+ require 'rcov/rcovtask'
46
+ Rcov::RcovTask.new do |test|
47
+ test.libs << 'test'
48
+ test.pattern = 'test/**/*_test.rb'
49
+ test.verbose = true
50
+ end
51
+ rescue LoadError
52
+ task :rcov do
53
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
54
+ end
55
+ end
56
+
57
+ task :test => :check_dependencies
58
+
59
+ begin
60
+ require 'reek/adapters/rake_task'
61
+ Reek::RakeTask.new do |t|
62
+ t.fail_on_error = true
63
+ t.verbose = false
64
+ t.source_files = 'lib/**/*.rb'
65
+ end
66
+ rescue LoadError
67
+ task :reek do
68
+ abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
69
+ end
70
+ end
71
+
72
+ begin
73
+ require 'roodi'
74
+ require 'roodi_task'
75
+ RoodiTask.new do |t|
76
+ t.verbose = false
77
+ end
78
+ rescue LoadError
79
+ task :roodi do
80
+ abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
81
+ end
82
+ end
83
+
84
+ task :default => :test
85
+
86
+ require 'rake/rdoctask'
87
+ Rake::RDocTask.new do |rdoc|
88
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
89
+
90
+ rdoc.rdoc_dir = 'rdoc'
91
+ rdoc.title = "memodis #{version}"
92
+ rdoc.rdoc_files.include('README*')
93
+ rdoc.rdoc_files.include('lib/**/*.rb')
94
+ end
95
+
96
+
97
+
98
+ # redis test environment...
99
+
100
+ def write_redis_config(confile, port, slaveof=nil)
101
+ pidfile = File.expand_path("test/pid/#{ File.basename(confile, '.*') }.pid")
102
+ logfile = File.expand_path("test/log/#{ File.basename(confile, '.*') }.log")
103
+ open(confile, 'w') do |f|
104
+ f.puts "daemonize yes"
105
+ f.puts "port #{port}"
106
+ f.puts "pidfile #{pidfile}"
107
+ f.puts "bind 127.0.0.1"
108
+ f.puts "timeout 0"
109
+ f.puts "dir ./test"
110
+ f.puts "loglevel notice"
111
+ f.puts "logfile #{logfile}"
112
+ f.puts slaveof unless slaveof.nil?
113
+ f.puts "databases 2"
114
+ f.puts "maxmemory 6291456"
115
+ f.puts "glueoutputbuf yes"
116
+ f.puts "shareobjects no"
117
+ f.puts "shareobjectspoolsize 1024"
118
+ end
119
+ end
120
+
121
+ directory 'test/config'
122
+ directory 'test/log'
123
+ directory 'test/pid'
124
+
125
+ (16379..16380).each do |port|
126
+ config_file = "test/config/redis-server:#{port}.conf"
127
+ file config_file => %w(test/config test/log test/pid) do
128
+ write_redis_config(config_file, port)
129
+ end
130
+ namespace :test do
131
+ task :config => config_file
132
+ end
133
+ end
134
+
135
+ (16389..16394).each do |port|
136
+ config_file = "test/config/redis-server:#{port}.conf"
137
+ file config_file => %w(test/config test/log test/pid) do
138
+ master_port = port.even? ? 16379 : 16380
139
+ write_redis_config(config_file, port, "slaveof 127.0.0.1 #{master_port}")
140
+ end
141
+ namespace :test do
142
+ task :config => config_file
143
+ end
144
+ end
145
+
146
+ task :test => 'test:config'
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/examples/main.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'pathname'
2
+ load Pathname.new(__FILE__).parent.parent + 'lib/memodis.rb'
3
+
4
+ require 'rubygems'
5
+ require 'hitimes'
6
+
7
+ def fib(num)
8
+ return num if num < 2
9
+ fib(num - 1) + fib(num - 2)
10
+ end
11
+
12
+ puts 'Before memoize: '
13
+ puts Hitimes::Interval.measure { print fib(33); print ': ' }
14
+
15
+
16
+ extend Memodis
17
+
18
+ memoize :fib, Memodis::DistCache.new({
19
+ :key_gen => lambda { |k| "fib(#{k})" },
20
+ :decoder => :integer,
21
+ :expires => (10 * 60),
22
+ :master => '127.0.0.1:16379 127.0.0.1:16380'.split,
23
+ :slaves => '127.0.0.1:16389 127.0.0.1:16390
24
+ 127.0.0.1:16391 127.0.0.1:16392
25
+ 127.0.0.1:16393 127.0.0.1:16394'.split
26
+ })
27
+
28
+ puts "\nAfter memoize: "
29
+ puts Hitimes::Interval.measure { print fib(33); print ': ' }
30
+ puts Hitimes::Interval.measure { print fib(33); print ': ' }
31
+ puts Hitimes::Interval.measure { print fib(33); print ': ' }
@@ -0,0 +1,10 @@
1
+
2
+ require "pathname"
3
+ vendor_path = Pathname.new(__FILE__).parent.parent + "vendor"
4
+ Pathname.glob("#{vendor_path}/**/lib") do |lib|
5
+ next if $LOAD_PATH.include?(lib)
6
+ $LOAD_PATH.insert(0, lib.realpath.to_s) if lib.directory?
7
+ end
8
+
9
+ require "dist_redis"
10
+
data/lib/memodis.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'pathname'
2
+ load Pathname.new(__FILE__).parent+'dependencies.rb'
3
+
4
+ require 'memodis/dist_cache'
5
+
6
+ module Memodis
7
+
8
+ # slurp cool vendor goodies into our namespace. would declare them
9
+ # as gem dependencies, but they don't seem to be published...
10
+
11
+ VENDOR_PATH = Pathname.new(__FILE__).parent.parent + 'vendor'
12
+
13
+ class_eval((VENDOR_PATH+'memoizable.rb').read) unless defined? Memodis::Memoizable
14
+ class_eval((VENDOR_PATH+'weak_cache.rb').read) unless defined? Memodis::WeakCache
15
+
16
+ include Memodis::Memoizable
17
+
18
+ end
@@ -0,0 +1,88 @@
1
+ module Memodis
2
+
3
+ class DistCache
4
+
5
+ CODERS = {}
6
+ CODERS.default = lambda { |v| v }
7
+ CODERS[:float] = lambda { |v| Float(v) }
8
+ CODERS[:integer] = lambda { |v| Integer(v) }
9
+ CODERS.freeze
10
+
11
+ def initialize(options)
12
+
13
+ @master = DistRedis.new({
14
+ :db => options[:db],
15
+ :hosts => options[:master],
16
+ :timeout => options[:timeout],
17
+ })
18
+
19
+ @slaves = options[:slaves].map do |h|
20
+ host, port = h.split(':')
21
+ Redis.new({
22
+ :db => options[:db],
23
+ :host => host,
24
+ :port => port,
25
+ :timeout => options[:timeout],
26
+ })
27
+ end
28
+
29
+ @encoder = case options[:encoder]
30
+ when Proc; options[:encoder]
31
+ else; CODERS[options[:encoder]]
32
+ end
33
+
34
+ @decoder = case options[:decoder]
35
+ when Proc; options[:decoder]
36
+ else; CODERS[options[:decoder]]
37
+ end
38
+
39
+ @key_gen = options.fetch(:key_gen, lambda { |k| k })
40
+
41
+ @expires = options[:expires]
42
+ end
43
+
44
+ def []= key, val
45
+ key = @key_gen.call(key)
46
+ @master.set(key, encode(val))
47
+ @master.expire(key, @expires) unless @expires.nil?
48
+ end
49
+
50
+ def [] key
51
+ key = @key_gen.call(key)
52
+ if val = get(key)
53
+ decode(val)
54
+ else
55
+ nil # don't decode a miss
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def indexed_slaves
62
+ @indexed_slaves ||= @slaves.inject(Hash.new) do |index, slave|
63
+ slave_info = slave.info
64
+ master_host = slave_info[:master_host]
65
+ master_port = slave_info[:master_port]
66
+ index["#{master_host}:#{master_port}"] = slave
67
+ index
68
+ end
69
+ end
70
+
71
+ def get key
72
+ m_node = @master.node_for_key(String(key.first))
73
+ s_node = indexed_slaves[m_node.server] || m_node
74
+ # TODO log warning if s_node == m_node
75
+ s_node.get(key)
76
+ end
77
+
78
+ def decode(val)
79
+ @decoder.call(val)
80
+ end
81
+
82
+ def encode(val)
83
+ @encoder.call(val)
84
+ end
85
+
86
+ end
87
+
88
+ end
@@ -0,0 +1,60 @@
1
+ require 'daemon_controller'
2
+ require 'socket'
3
+
4
+ module Memodis
5
+ class LocalRedisController
6
+
7
+ def self.start(config_file)
8
+ new(config_file).start
9
+ end
10
+
11
+ attr_reader :config, :controller
12
+
13
+ def initialize(config_file)
14
+ config = IO.readlines(config_file)
15
+ config.reject! { |l| l !~ /^(bind|port|pidfile|logfile)[ \t]?/ }
16
+ config.collect! { |l| l.chomp.split(/[ \t]/) }
17
+ config.flatten!
18
+ config = Hash[*config]
19
+ config['file'] = config_file
20
+ @config = config
21
+ @config.freeze
22
+
23
+ @controller = DaemonController.new({
24
+ :identifier => identifier,
25
+ :start_command => start_command,
26
+ :ping_command => ping_command,
27
+ :pid_file => pid_file,
28
+ :log_file => log_file
29
+ })
30
+ end
31
+
32
+ def connect
33
+ @controller.connect { TCPSocket.new(@config['bind'], @config['port']) }
34
+ end
35
+ alias :start :connect
36
+
37
+ private
38
+
39
+ def identifier
40
+ "redis-server #{@config['file']}"
41
+ end
42
+
43
+ def start_command
44
+ "redis-server #{@config['file']}"
45
+ end
46
+
47
+ def ping_command
48
+ lambda { TCPSocket.new(@config['bind'], @config['port']) }
49
+ end
50
+
51
+ def pid_file
52
+ @config['pidfile']
53
+ end
54
+
55
+ def log_file
56
+ @config['logfile']
57
+ end
58
+
59
+ end
60
+ end
data/script/mooch ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ require "pathname"
3
+
4
+
5
+ LOADER = '''
6
+ require "pathname"
7
+
8
+ vendor_path = Pathname.new(__FILE__).parent.parent + 'vendor'
9
+
10
+ Pathname.glob("#{vendor_path}/**/lib") do |lib|
11
+ next if $LOAD_PATH.include?(lib)
12
+ $LOAD_PATH.insert(0, lib.realpath.to_s) if lib.directory?
13
+ end
14
+ '''
15
+
16
+
17
+ if ARGV[0] == "init"
18
+ lib = Pathname.new(ARGV[1])
19
+ lib.mkpath
20
+ (lib + 'dependencies.rb').open("w") do |file|
21
+ file.write LOADER
22
+ end
23
+ else
24
+ vendor = Pathname.new("vendor")
25
+ vendor.mkpath
26
+
27
+ if File.exist?('.git')
28
+ system("git add vendor")
29
+ system("git submodule add git://github.com/#{ARGV[0]}.git vendor/#{ARGV[0]}")
30
+ else
31
+ Dir.chdir(vendor.realpath)
32
+ system("git clone git://github.com/#{ARGV[0]}.git #{ARGV[0]}")
33
+ if ARGV[1]
34
+ Dir.chdir(ARGV[0])
35
+ system("git checkout #{ARGV[1]}")
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env watchr
2
+
3
+ watch( 'test/teststrap.rb' ) { |md| system("ruby -Ilib -Itest test/*_test.rb") }
4
+
5
+ watch( 'test/.*_test.rb' ) { |md| system("ruby -Ilib -Itest #{md[0]}") }
6
+
7
+ watch( 'lib/(.*)\.rb' ) { |md| system("ruby -Ilib -Itest test/#{md[1]}_test.rb") }
@@ -0,0 +1,12 @@
1
+ require 'teststrap'
2
+
3
+ context Memodis do
4
+ setup { Memodis }
5
+ asserts(:constants).includes('Memoizable')
6
+ asserts(:constants).includes('WeakCache')
7
+ end
8
+
9
+ context "Exteding Memodis" do
10
+ setup { Object.new.extend(Memodis) }
11
+ asserts(:methods).includes('memoize')
12
+ end
data/test/teststrap.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'riot'
3
+ require 'memodis'
4
+ Riot.dots
5
+
6
+
7
+ require 'memodis/local_redis_controller'
8
+ Dir['test/config/redis-server*.conf'].each do |config_file|
9
+ Memodis::LocalRedisController.start(config_file)
10
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # memoizable.rb
4
+ #
5
+ # Created by James Edward Gray II on 2006-01-21.
6
+ # Copyright 2006 Gray Productions. All rights reserved.
7
+
8
+ #
9
+ # Have your class or module <tt>extend Memoizable</tt> to gain access to the
10
+ # #memoize method.
11
+ #
12
+ module Memoizable
13
+ #
14
+ # This method is used to replace a computationally expensive method with an
15
+ # equivalent method that will answer repeat calls for indentical arguments
16
+ # from a _cache_. To use, make sure the current class extends Memoizable,
17
+ # then call by passing the _name_ of the method you wish to cache results for.
18
+ #
19
+ # The _cache_ object can be any object supporting both #[] and #[]=. The keys
20
+ # used for the _cache_ are an Array of the arguments the method was called
21
+ # with and the values are just the returned results of the original method
22
+ # call. The default _cache_ is a simple Hash, providing in-memory storage.
23
+ #
24
+ def memoize( name, cache = Hash.new )
25
+ original = "__unmemoized_#{name}__"
26
+
27
+ #
28
+ # <tt>self.class</tt> is used for the top level, to modify Object, otherwise
29
+ # we just modify the Class or Module directly
30
+ #
31
+ ([Class, Module].include?(self.class) ? self : self.class).class_eval do
32
+ alias_method original, name
33
+ private original
34
+ define_method(name) { |*args| cache[args] ||= send(original, *args) }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ #
2
+ # This cache uses weak references, which can be garbage collected. When the
3
+ # cache is checked, the value will be returned if it is still around, otherwise
4
+ # +nil+ is returned.
5
+ #
6
+ # (Code by Mauricio Fernandez.)
7
+ #
8
+ class WeakCache
9
+ def initialize
10
+ set_internal_hash
11
+ end
12
+
13
+ def method_missing( meth, *args, &block )
14
+ __get_hash__.send(meth, *args, &block)
15
+ end
16
+
17
+ private
18
+
19
+ def __get_hash__
20
+ old_critical = Thread.critical
21
+ Thread.critical = true
22
+
23
+ @valid or set_internal_hash
24
+ return ObjectSpace._id2ref(@hash_id)
25
+ ensure
26
+ Thread.critical = old_critical
27
+ end
28
+
29
+ def set_internal_hash
30
+ hash = Hash.new
31
+ @hash_id = hash.object_id
32
+ @valid = true
33
+
34
+ ObjectSpace.define_finalizer(hash, lambda { @valid = false })
35
+ hash = nil
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: memodis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - levicook@gmail.com
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-14 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: riot
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: reek
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: daemon_controller
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ description: |
46
+
47
+ semi-transparent memoization; backed by redis; redis-rb and redis-namespace
48
+
49
+ Background
50
+ -----------
51
+ 1) http://blog.grayproductions.net/articles/caching_and_memoization
52
+ 2) http://code.google.com/p/redis & http://github.com/ezmobius/redis-rb
53
+
54
+ Important Moving Parts
55
+ ----------------------
56
+ 1) http://code.google.com/p/redis/wiki/GetCommand
57
+ 2) http://code.google.com/p/redis/wiki/SetCommand
58
+ 3) http://code.google.com/p/redis/wiki/SetnxCommand
59
+ 4) http://github.com/defunkt/redis-namespace
60
+
61
+ email: levicook@gmail.com
62
+ executables: []
63
+
64
+ extensions: []
65
+
66
+ extra_rdoc_files:
67
+ - LICENSE
68
+ - README.rdoc
69
+ files:
70
+ - .document
71
+ - .gitignore
72
+ - .gitmodules
73
+ - LICENSE
74
+ - README.rdoc
75
+ - Rakefile
76
+ - VERSION
77
+ - examples/main.rb
78
+ - lib/dependencies.rb
79
+ - lib/memodis.rb
80
+ - lib/memodis/dist_cache.rb
81
+ - lib/memodis/local_redis_controller.rb
82
+ - script/mooch
83
+ - script/test.watchr
84
+ - test/memodis_test.rb
85
+ - test/teststrap.rb
86
+ - vendor/memoizable.rb
87
+ - vendor/weak_cache.rb
88
+ has_rdoc: true
89
+ homepage: http://github.com/levicook/memodis
90
+ licenses: []
91
+
92
+ post_install_message:
93
+ rdoc_options:
94
+ - --charset=UTF-8
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: "0"
102
+ version:
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: "0"
108
+ version:
109
+ requirements: []
110
+
111
+ rubyforge_project:
112
+ rubygems_version: 1.3.5
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: redis backed memoization helpers
116
+ test_files:
117
+ - test/memodis_test.rb
118
+ - test/teststrap.rb
119
+ - examples/main.rb