binary42-fastcache 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/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
|