binary42-fastcache 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README +4 -0
- data/Rakefile +17 -0
- data/benchmark/benchmark_helper.rb +6 -0
- data/benchmark/connection.rb +15 -0
- data/lib/fastcache/bucket/consistent.rb +3 -0
- data/lib/fastcache/bucket/modulus.rb +21 -0
- data/lib/fastcache/hash/crc32.rb +30 -0
- data/lib/fastcache/hash/md5.rb +3 -0
- data/lib/fastcache/hash/sha1.rb +3 -0
- data/lib/fastcache/interface/connection.rb +24 -0
- data/lib/fastcache/interface/dictionary.rb +3 -0
- data/lib/fastcache/interface/evaluator.rb +3 -0
- data/lib/fastcache/marshal/duck.rb +0 -0
- data/lib/fastcache/marshal/ruby.rb +0 -0
- data/lib/fastcache/marshal/yaml.rb +0 -0
- data/lib/fastcache/memcache/node.rb +70 -0
- data/lib/fastcache/memcache/protocol.rb +105 -0
- data/lib/fastcache/memcache.rb +8 -0
- data/lib/fastcache/router/basic.rb +0 -0
- data/lib/fastcache/util/maybe.rb +59 -0
- data/lib/fastcache.rb +68 -0
- data/spec/memcache/node_spec.rb +46 -0
- data/spec/memcache/protocol_spec.rb +1 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/util/maybe_spec.rb +95 -0
- metadata +81 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2008 Upswing CRM, LLC
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,4 @@
|
|
1
|
+
FastCache
|
2
|
+
=========
|
3
|
+
|
4
|
+
FastCache is a simple client library implementing the memcache protocol. It currently aims to support all documented commands and features in version 1.2.6. Secondary goals include support for a modular serialization and node selection system as well as some convenient utility classes for wrapping memcached clusters.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rake/rdoctask'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
task :default => :spec
|
5
|
+
|
6
|
+
desc 'Run specs'
|
7
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
8
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
9
|
+
end
|
10
|
+
|
11
|
+
Rake::RDocTask.new do |task|
|
12
|
+
task.rdoc_dir = 'doc'
|
13
|
+
task.title = 'FastCache - memcache client protocol implementation'
|
14
|
+
task.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
15
|
+
task.rdoc_files.include 'README'
|
16
|
+
task.rdoc_files.include 'lib/**/*.rb'
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
n = 1000
|
2
|
+
Benchmark.bm(30) do |x|
|
3
|
+
x.report 'manual connection open/close' do
|
4
|
+
n.times do
|
5
|
+
conn = FastCache.connect NODE
|
6
|
+
conn.close
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
x.report 'block connection open/close' do
|
11
|
+
n.times do
|
12
|
+
FastCache.connect(NODE) {}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class FastCache::ModulusBucket
|
2
|
+
|
3
|
+
def initialize(key, hash)
|
4
|
+
@digest = hash.new(key).hexdigest.to_i(16)
|
5
|
+
@key = key
|
6
|
+
end
|
7
|
+
|
8
|
+
def select(nodes)
|
9
|
+
count = nodes.size
|
10
|
+
# Note: An upper bound on tries ought to be given at some point
|
11
|
+
count.times do |i|
|
12
|
+
begin
|
13
|
+
break yield(@key, nodes[(@digest + i) % count])
|
14
|
+
rescue FastCache::Memcache::ProtocolError
|
15
|
+
next
|
16
|
+
end
|
17
|
+
raise 'Unable to find suitable node to communicate with'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class FastCache::CRC32
|
2
|
+
|
3
|
+
def initialize(string = nil)
|
4
|
+
@crc = 0xFFFFFFFF
|
5
|
+
self << string if string
|
6
|
+
end
|
7
|
+
|
8
|
+
def <<(string)
|
9
|
+
string.size.times do |i|
|
10
|
+
@crc ^= string[i]
|
11
|
+
8.times do
|
12
|
+
if (@crc & 1).zero?
|
13
|
+
@crc >>= 1
|
14
|
+
else
|
15
|
+
@crc = (@crc >> 1) ^ 0xEDB88320
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def digest
|
23
|
+
[@crc ^ 0xFFFFFFFF].pack('N')
|
24
|
+
end
|
25
|
+
|
26
|
+
def hexdigest
|
27
|
+
(@crc ^ 0xFFFFFFFF).to_s(16)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class FastCache::Connection
|
2
|
+
include FastCache::Memcache::Protocol
|
3
|
+
|
4
|
+
attr_accessor :bucket_class, :hash_class, :marshal_module
|
5
|
+
attr_reader :nodes
|
6
|
+
|
7
|
+
def initialize(nodes, opts)
|
8
|
+
@nodes = nodes.map {|(host, port)| FastCache::Memcache::Node.new(host, port)}
|
9
|
+
@bucket_class = FastCache::ModulusBucket
|
10
|
+
@hash_class = FastCache::CRC32
|
11
|
+
@marshal_module = Marshal # Ruby's built in marshal
|
12
|
+
end
|
13
|
+
|
14
|
+
def close
|
15
|
+
@nodes.each do |node| node.close end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def bucket(key)
|
21
|
+
@bucket_class.new(key, @hash_class)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
class FastCache::Memcache::Node
|
5
|
+
attr_reader :host, :port, :status
|
6
|
+
attr_accessor :weight
|
7
|
+
|
8
|
+
CONNECT_TIMEOUT = 0.25
|
9
|
+
RETRY_DELAY = 10.0
|
10
|
+
|
11
|
+
def initialize(host, port)
|
12
|
+
@host = host
|
13
|
+
@port = port
|
14
|
+
@weight = 1
|
15
|
+
@status = :disconnected
|
16
|
+
@socket = nil
|
17
|
+
@retry = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def read(*a)
|
21
|
+
socket.read(*a)
|
22
|
+
end
|
23
|
+
|
24
|
+
def write(data)
|
25
|
+
socket.write(data)
|
26
|
+
end
|
27
|
+
|
28
|
+
def gets
|
29
|
+
socket.gets
|
30
|
+
end
|
31
|
+
|
32
|
+
def close
|
33
|
+
socket.close
|
34
|
+
@socket = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def connect
|
38
|
+
socket
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def socket
|
44
|
+
return @socket if @socket and not @socket.closed?
|
45
|
+
|
46
|
+
@socket = nil
|
47
|
+
|
48
|
+
# If the host was dead, don't retry for a while.
|
49
|
+
return if @retry and @retry > Time.now
|
50
|
+
|
51
|
+
# Attempt to connect if not already connected.
|
52
|
+
begin
|
53
|
+
@socket = timeout CONNECT_TIMEOUT do
|
54
|
+
TCPSocket.new @host, @port
|
55
|
+
end
|
56
|
+
@retry = nil
|
57
|
+
@status = :connected
|
58
|
+
rescue SocketError, SystemCallError, IOError, Timeout::Error => err
|
59
|
+
@status = :dead
|
60
|
+
@socket = nil
|
61
|
+
@retry = Time.now + RETRY_DELAY
|
62
|
+
end
|
63
|
+
|
64
|
+
@socket
|
65
|
+
rescue IOError
|
66
|
+
@socket = nil
|
67
|
+
retry
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# Informal protocol for mixin:
|
2
|
+
# bucket will ask for a key and return a bucket
|
3
|
+
# nodes will return connection nodes
|
4
|
+
module FastCache::Memcache::Protocol
|
5
|
+
|
6
|
+
def get(key)
|
7
|
+
bucket(key).select(nodes) {|node_key, node|
|
8
|
+
node.write "get #{node_key}\r\n"
|
9
|
+
resp = node.gets
|
10
|
+
break FastCache::Maybe.nothing if resp == "END\r\n"
|
11
|
+
resp =~ /(\d+)\r/
|
12
|
+
value = node.read $1.to_i
|
13
|
+
node.read 2 # \r\n
|
14
|
+
node.gets # END\r\n
|
15
|
+
break FastCache::Maybe.just(value) unless value.nil?
|
16
|
+
FastCache::Maybe.nothing
|
17
|
+
}
|
18
|
+
rescue FastCache::Memcache::ProtocolError
|
19
|
+
FastCache::Maybe.nothing
|
20
|
+
end
|
21
|
+
|
22
|
+
def set(key, value, expiry = 0)
|
23
|
+
value = value.to_s
|
24
|
+
bucket(key).select(nodes) {|node_key, node|
|
25
|
+
node.write "set #{node_key} 0 #{expiry} #{value.size}\r\n#{value}\r\n"
|
26
|
+
result = node.gets
|
27
|
+
if result == "STORED\r\n"
|
28
|
+
FastCache::Maybe.just(value)
|
29
|
+
else
|
30
|
+
FastCache::Maybe.nothing
|
31
|
+
end
|
32
|
+
}
|
33
|
+
rescue FastCache::Memcache::ProtocolError
|
34
|
+
FastCache::Maybe.nothing
|
35
|
+
end
|
36
|
+
|
37
|
+
def add(key, value, expiry = 0)
|
38
|
+
data = value.to_s
|
39
|
+
bucket(key).select(nodes) {|node_key, node|
|
40
|
+
node.write "add #{node_key} 0 #{expiry} #{data.size}\r\n#{data}\r\n"
|
41
|
+
resp = node.gets
|
42
|
+
if resp == "STORED\r\n"
|
43
|
+
FastCache::Maybe.just(data)
|
44
|
+
else
|
45
|
+
FastCache::Maybe.nothing
|
46
|
+
end
|
47
|
+
}
|
48
|
+
rescue FastCache::Memcache::ProtocolError
|
49
|
+
FastCache::Maybe.nothing
|
50
|
+
end
|
51
|
+
|
52
|
+
def replace(key, value, expiry = 0)
|
53
|
+
data = value.to_s
|
54
|
+
bucket(key).select(nodes) {|node_key, node|
|
55
|
+
node.write "replace #{node_key} 0 #{expiry} #{data.size}\r\n#{data}\r\n"
|
56
|
+
resp = node.gets
|
57
|
+
if resp == "STORED\r\n"
|
58
|
+
FastCache::Maybe.just(data)
|
59
|
+
else
|
60
|
+
FastCache::Maybe.nothing
|
61
|
+
end
|
62
|
+
}
|
63
|
+
rescue FastCache::Memcache::ProtocolError
|
64
|
+
FastCache::Maybe.nothing
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete(key, expiry = 0)
|
68
|
+
bucket(key).select(nodes) {|node_key, node|
|
69
|
+
node.write "delete #{node_key} #{expiry}\r\n"
|
70
|
+
resp = node.gets
|
71
|
+
resp == "DELETED\r\n"
|
72
|
+
}
|
73
|
+
rescue FastCache::Memcache::Protocol
|
74
|
+
FastCache::Maybe.nothing
|
75
|
+
end
|
76
|
+
|
77
|
+
def cas(*)
|
78
|
+
fail 'Not implemented'
|
79
|
+
end
|
80
|
+
|
81
|
+
def incr(*)
|
82
|
+
fail 'Not implemented'
|
83
|
+
end
|
84
|
+
|
85
|
+
def decr(*)
|
86
|
+
fail 'Not implemented'
|
87
|
+
end
|
88
|
+
|
89
|
+
def flush_all_caches
|
90
|
+
fail 'Not implemented'
|
91
|
+
end
|
92
|
+
|
93
|
+
def flush_cache(key)
|
94
|
+
fail 'Not implemented'
|
95
|
+
end
|
96
|
+
|
97
|
+
def stats(*)
|
98
|
+
fail 'Not implemented'
|
99
|
+
end
|
100
|
+
|
101
|
+
def reset_connections
|
102
|
+
fail 'Not implemented yet'
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
File without changes
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class FastCache::Maybe
|
2
|
+
|
3
|
+
def self.nothing
|
4
|
+
self.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.just(x)
|
8
|
+
self.new(x)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(*args)
|
12
|
+
args.size < 2 or raise ArgumentError,
|
13
|
+
"#{self.class.name}#initialize only takes 0 or 1 arguments. Given #{args.size}."
|
14
|
+
if args.empty?
|
15
|
+
@value = nil
|
16
|
+
@nothing = true
|
17
|
+
else
|
18
|
+
@value = *args
|
19
|
+
@nothing = false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def anything?
|
24
|
+
!@nothing
|
25
|
+
end
|
26
|
+
|
27
|
+
def join(other)
|
28
|
+
(other.nothing? || self.nothing?) ?
|
29
|
+
Maybe.nothing :
|
30
|
+
other
|
31
|
+
end
|
32
|
+
|
33
|
+
def nothing?
|
34
|
+
@nothing
|
35
|
+
end
|
36
|
+
alias empty? nothing?
|
37
|
+
alias blank? nothing?
|
38
|
+
|
39
|
+
def value
|
40
|
+
if anything?
|
41
|
+
@value
|
42
|
+
else
|
43
|
+
raise 'No value set'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def value=(obj)
|
48
|
+
@nothing = false
|
49
|
+
@value = obj
|
50
|
+
end
|
51
|
+
|
52
|
+
def void
|
53
|
+
@nothing = true
|
54
|
+
@value = nil
|
55
|
+
self
|
56
|
+
end
|
57
|
+
alias clear! void
|
58
|
+
|
59
|
+
end
|
data/lib/fastcache.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module FastCache; end
|
2
|
+
|
3
|
+
# Utilities and support
|
4
|
+
require 'fastcache/util/maybe'
|
5
|
+
|
6
|
+
# Bucketing algorithms
|
7
|
+
require 'fastcache/bucket/consistent'
|
8
|
+
require 'fastcache/bucket/modulus'
|
9
|
+
|
10
|
+
# Key hashing algorithms
|
11
|
+
require 'fastcache/hash/crc32'
|
12
|
+
require 'fastcache/hash/md5'
|
13
|
+
require 'fastcache/hash/sha1'
|
14
|
+
|
15
|
+
# Key routers
|
16
|
+
# TODO: Check for ruby-ketama
|
17
|
+
require 'fastcache/router/basic'
|
18
|
+
|
19
|
+
# Networking and protocol support code
|
20
|
+
require 'fastcache/memcache'
|
21
|
+
|
22
|
+
# Interfaces
|
23
|
+
require 'fastcache/interface/connection'
|
24
|
+
require 'fastcache/interface/dictionary'
|
25
|
+
require 'fastcache/interface/evaluator'
|
26
|
+
|
27
|
+
class << FastCache
|
28
|
+
|
29
|
+
def connect(node_list = 'localhost:11211', opts = {})
|
30
|
+
opts = {
|
31
|
+
:name => 'default',
|
32
|
+
:type => 'raw'
|
33
|
+
}.merge(opts)
|
34
|
+
|
35
|
+
connections[opts[:name]].close if connections[opts[:name]]
|
36
|
+
|
37
|
+
nodes = node_list.split(',').map {|node|
|
38
|
+
host, port = *node.split(':')
|
39
|
+
port ||= 80
|
40
|
+
[host, port.to_i]
|
41
|
+
}
|
42
|
+
|
43
|
+
connections[opts[:name]] = case(opts[:type])
|
44
|
+
when FastCache::Connection
|
45
|
+
opts[:type].new(nodes, opts[:connection])
|
46
|
+
when 'dictionary'
|
47
|
+
FastCache::Dictionary.new(nodes, opts[:connection])
|
48
|
+
when 'evaluator'
|
49
|
+
FastCache::Evaluator.new(nodes, opts[:connection])
|
50
|
+
when 'raw'
|
51
|
+
FastCache::Connection.new(nodes, opts[:connection])
|
52
|
+
else
|
53
|
+
raise "Unknown connection type #{opts[:type]}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def connection(name = 'default')
|
58
|
+
connections[name] or raise ArgumentError,
|
59
|
+
"Invalid connection name #{name.inspect}"
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def connections
|
65
|
+
@connections ||= {}
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe FastCache::Memcache::Node, 'initialize' do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@node = FastCache::Memcache::Node.new('localhost', 11211)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should take host and port as arguments' do
|
10
|
+
@node.host.should == 'localhost'
|
11
|
+
@node.port.should == 11211
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should set a default weight of 1' do
|
15
|
+
@node.weight.should == 1
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should set a default status of disconnected' do
|
19
|
+
@node.status.should == :disconnected
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
describe FastCache::Memcache::Node, 'connect' do
|
25
|
+
|
26
|
+
before :each do
|
27
|
+
@conn = FastCache::Memcache::Node.new('localhost', 11211)
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '(success)' do
|
31
|
+
it 'should set the status to :connected' do
|
32
|
+
TCPSocket.should_receive(:new).with(@conn.host, @conn.port)
|
33
|
+
@conn.connect
|
34
|
+
@conn.status.should == :connected
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '(failure)' do
|
39
|
+
it 'should set the status to :dead' do
|
40
|
+
TCPSocket.should_receive(:new).with(@conn.host, @conn.port).and_raise(Errno::ECONNREFUSED)
|
41
|
+
@conn.connect
|
42
|
+
@conn.status.should == :dead
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
include FastCache
|
3
|
+
|
4
|
+
describe Maybe, '.nothing' do
|
5
|
+
|
6
|
+
it 'should return an object with nothing' do
|
7
|
+
Maybe.nothing.should be_nothing
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
describe Maybe, '.just' do
|
13
|
+
|
14
|
+
it 'should return an object with the passed value' do
|
15
|
+
Maybe.just(42).value.should == 42
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
describe Maybe, '#anything?' do
|
21
|
+
|
22
|
+
it 'should return true if there is a value' do
|
23
|
+
Maybe.just(42).should be_anything
|
24
|
+
Maybe.just(nil).should be_anything
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should return false if there is no value' do
|
28
|
+
Maybe.nothing.should_not be_anything
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
describe Maybe, '#nothing?' do
|
34
|
+
|
35
|
+
it 'should return false if there is a value' do
|
36
|
+
Maybe.just(42).should_not be_nothing
|
37
|
+
Maybe.just(nil).should_not be_nothing
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should return true if there is no value' do
|
41
|
+
Maybe.nothing.should be_nothing
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe Maybe, 'getting and setting wrapped values' do
|
47
|
+
|
48
|
+
it 'should allow setting a value on nothing' do
|
49
|
+
[:hello, nil, false, true].each do |val|
|
50
|
+
what = Maybe.nothing
|
51
|
+
what.value = val
|
52
|
+
what.should be_anything
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should should replace existing values' do
|
57
|
+
[:hello, nil, false, true].each do |val|
|
58
|
+
what = Maybe.just(42)
|
59
|
+
what.value = val
|
60
|
+
what.value.should == val
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe Maybe, '#join' do
|
67
|
+
|
68
|
+
it 'should keep nothing if nothing' do
|
69
|
+
Maybe.nothing.join(Maybe.nothing).should be_nothing
|
70
|
+
Maybe.nothing.join(Maybe.just(42)).should be_nothing
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should keep the new anything if anything' do
|
74
|
+
what = Maybe.just(42).join(Maybe.just(43))
|
75
|
+
what.should be_anything
|
76
|
+
what.value.should == 43
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should become nothing if anything is passed nothing' do
|
80
|
+
Maybe.just(42).join(Maybe.nothing).should be_nothing
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
describe Maybe, '#void' do
|
86
|
+
|
87
|
+
it 'should make an object with anything into one with nothing' do
|
88
|
+
Maybe.just(42).void.should be_nothing
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should not let nothing remain nothing' do
|
92
|
+
Maybe.nothing.void.should be_nothing
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: binary42-fastcache
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Mitchell
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-10-27 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: FastCache implements a flexible client side implementation of the memcached protocol. The library comes with a light core as well as some components to wrap the protocol with more advanced behaviors.
|
17
|
+
email: brian.mitchell@excoventures.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/fastcache/hash/sha1.rb
|
26
|
+
- lib/fastcache/hash/md5.rb
|
27
|
+
- lib/fastcache/hash/crc32.rb
|
28
|
+
- lib/fastcache/util/maybe.rb
|
29
|
+
- lib/fastcache/marshal/yaml.rb
|
30
|
+
- lib/fastcache/marshal/duck.rb
|
31
|
+
- lib/fastcache/marshal/ruby.rb
|
32
|
+
- lib/fastcache/bucket/modulus.rb
|
33
|
+
- lib/fastcache/bucket/consistent.rb
|
34
|
+
- lib/fastcache/router/basic.rb
|
35
|
+
- lib/fastcache/interface/dictionary.rb
|
36
|
+
- lib/fastcache/interface/evaluator.rb
|
37
|
+
- lib/fastcache/interface/connection.rb
|
38
|
+
- lib/fastcache/memcache.rb
|
39
|
+
- lib/fastcache/memcache/node.rb
|
40
|
+
- lib/fastcache/memcache/protocol.rb
|
41
|
+
- lib/fastcache.rb
|
42
|
+
- LICENSE
|
43
|
+
- Rakefile
|
44
|
+
- README
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/binary42/fastcache
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- --line-numbers
|
50
|
+
- --inline-source
|
51
|
+
- --main
|
52
|
+
- README
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.2.0
|
71
|
+
signing_key:
|
72
|
+
specification_version: 2
|
73
|
+
summary: FastCache - a client side implementation of the memcached protocol
|
74
|
+
test_files:
|
75
|
+
- spec/util/maybe_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
- spec/memcache/node_spec.rb
|
78
|
+
- spec/memcache/protocol_spec.rb
|
79
|
+
- benchmark/connection.rb
|
80
|
+
- benchmark/benchmark_helper.rb
|
81
|
+
- Rakefile
|