mogilefs-client 3.9.0 → 3.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitattributes +4 -0
- data/.olddoc.yml +2 -0
- data/GIT-VERSION-GEN +1 -1
- data/README +1 -1
- data/lib/mogilefs.rb +2 -0
- data/lib/mogilefs/admin.rb +3 -2
- data/lib/mogilefs/backend.rb +14 -9
- data/lib/mogilefs/bigfile.rb +1 -1
- data/lib/mogilefs/bigfile/filter.rb +2 -2
- data/lib/mogilefs/chunker.rb +2 -3
- data/lib/mogilefs/client.rb +3 -1
- data/lib/mogilefs/mogilefs.rb +6 -2
- data/lib/mogilefs/new_file/stream.rb +1 -1
- data/lib/mogilefs/socket/pure_ruby.rb +76 -35
- data/pkg.mk +1 -1
- data/test/fresh.rb +1 -1
- data/test/test_backend.rb +16 -1
- data/test/test_fresh.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d02264aa8b34f8fab16add61f2e81ee0643127cf
|
4
|
+
data.tar.gz: 5b333aa4abe0f863d819a9a480fa11180b902f1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6d650e02544bf8b432c80d50c21b13fbdca9169cd69b5513b534e66b7dc279f25a81a7b8823233e7e2b844d4624dd7d8c06ed38f74212d79a386884b8621f30
|
7
|
+
data.tar.gz: bd840975cb4154cc4aa6a0370765a7ff48dbf5374daa8aef079a48a2de9b27c4c3a96a543c9c52c6a2de95324fe8271b3a98818d41cc9d55edf8dc03890c0b09
|
data/.gitattributes
ADDED
data/.olddoc.yml
CHANGED
@@ -5,3 +5,5 @@ rdoc_url: http://bogomips.org/mogilefs-client
|
|
5
5
|
changelog_start: v1.2.1
|
6
6
|
private_email: e@80x24.org
|
7
7
|
public_email: mogilefs-client-public@bogomips.org
|
8
|
+
nntp_url: nntp://news.public-inbox.org/inbox.comp.file-systems.mogilefs.ruby
|
9
|
+
ml_url: http://bogomips.org/mogilefs-client-public/
|
data/GIT-VERSION-GEN
CHANGED
data/README
CHANGED
@@ -14,7 +14,7 @@ list-cc :: mailto:mogile@googlegroups.com
|
|
14
14
|
list-archive :: http://bogomips.org/mogilefs-client-public
|
15
15
|
email :: mailto:e@80x24.org
|
16
16
|
repo :: git://bogomips.org/mogilefs-client.git
|
17
|
-
|
17
|
+
http://bogomips.org/mogilefs-client.git
|
18
18
|
gitweb :: http://repo.or.cz/w/ruby-mogilefs-client.git
|
19
19
|
download :: http://bogomips.org/mogilefs-client/files/
|
20
20
|
|
data/lib/mogilefs.rb
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
# Client usage information is available in MogileFS::MogileFS.
|
6
6
|
module MogileFS
|
7
7
|
|
8
|
+
# :stopdoc:
|
8
9
|
if defined?(Process::CLOCK_MONOTONIC)
|
9
10
|
def self.now
|
10
11
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
@@ -14,6 +15,7 @@ def self.now
|
|
14
15
|
Time.now.to_f
|
15
16
|
end
|
16
17
|
end
|
18
|
+
# :startdoc:
|
17
19
|
|
18
20
|
# Standard error class for most MogileFS-specific errors
|
19
21
|
Error = Class.new(StandardError)
|
data/lib/mogilefs/admin.rb
CHANGED
@@ -69,8 +69,7 @@ def get_devices(devid = nil)
|
|
69
69
|
rv = clean('devices', 'dev', rv, true, to_i, want)
|
70
70
|
|
71
71
|
rv.each do |row|
|
72
|
-
u = row["utilization"] and
|
73
|
-
row["utilization"] = nil == u ? nil : u.to_f
|
72
|
+
u = row["utilization"] and row["utilization"] = u.to_f
|
74
73
|
|
75
74
|
case row["observed_state"]
|
76
75
|
when ""
|
@@ -83,6 +82,8 @@ def get_devices(devid = nil)
|
|
83
82
|
row["reject_bad_md5"] = true
|
84
83
|
when "0"
|
85
84
|
row["reject_bad_md5"] = false
|
85
|
+
when ""
|
86
|
+
row["reject_bad_md5"] = nil
|
86
87
|
end
|
87
88
|
end
|
88
89
|
end
|
data/lib/mogilefs/backend.rb
CHANGED
@@ -79,6 +79,7 @@ def initialize(args)
|
|
79
79
|
|
80
80
|
@mutex = Mutex.new
|
81
81
|
@timeout = args[:timeout] || 3
|
82
|
+
@connect_timeout = args[:connect_timeout] || @timeout
|
82
83
|
@socket = nil
|
83
84
|
@lasterr = nil
|
84
85
|
@lasterrstr = nil
|
@@ -242,7 +243,7 @@ def do_request(cmd, args, idempotent = false)
|
|
242
243
|
begin
|
243
244
|
io = dispatch_unlocked(request)
|
244
245
|
line = io.timed_gets(@timeout)
|
245
|
-
break if /\
|
246
|
+
break if /\n\z/ =~ line
|
246
247
|
|
247
248
|
line and raise MogileFS::InvalidResponseError,
|
248
249
|
"Invalid response from server: #{line.inspect}"
|
@@ -313,7 +314,7 @@ def clear_cache(types = %w(all))
|
|
313
314
|
types.each { |type| opts[type] = 1 }
|
314
315
|
|
315
316
|
sockets = @hosts.map do |host|
|
316
|
-
MogileFS::Socket.start(*(host.split(
|
317
|
+
MogileFS::Socket.start(*(host.split(':'.freeze))) rescue nil
|
317
318
|
end
|
318
319
|
sockets.compact!
|
319
320
|
|
@@ -346,8 +347,8 @@ def socket
|
|
346
347
|
next if dead = @dead[host] and dead[0] > (MogileFS.now - @fail_timeout)
|
347
348
|
|
348
349
|
begin
|
349
|
-
addr, port = host.split(
|
350
|
-
@socket = MogileFS::Socket.tcp(addr, port, @
|
350
|
+
addr, port = host.split(':'.freeze)
|
351
|
+
@socket = MogileFS::Socket.tcp(addr, port, @connect_timeout)
|
351
352
|
@active_host = host
|
352
353
|
rescue SystemCallError, MogileFS::Timeout => err
|
353
354
|
@dead[host] = [ MogileFS.now, err ]
|
@@ -365,8 +366,8 @@ def socket
|
|
365
366
|
# Turns a url params string into a Hash.
|
366
367
|
def url_decode(str) # :nodoc:
|
367
368
|
rv = {}
|
368
|
-
str.split(
|
369
|
-
k, v = pair.split(
|
369
|
+
str.split('&'.freeze).each do |pair|
|
370
|
+
k, v = pair.split('='.freeze, 2).map! { |x| url_unescape(x) }
|
370
371
|
rv[k.freeze] = v
|
371
372
|
end
|
372
373
|
rv
|
@@ -382,13 +383,15 @@ def url_decode(str) # :nodoc:
|
|
382
383
|
def url_encode(params) # :nodoc:
|
383
384
|
params.map do |k,v|
|
384
385
|
"#{url_escape k.to_s}=#{url_escape v.to_s}"
|
385
|
-
end.join(
|
386
|
+
end.join('&'.freeze)
|
386
387
|
end
|
387
388
|
|
388
389
|
# Escapes naughty URL characters.
|
389
390
|
if ''.respond_to?(:ord) # Ruby 1.9
|
390
391
|
def url_escape(str) # :nodoc:
|
391
|
-
str.gsub(/([^\w\,\-.\/\\\: ])/) { "%%%02x" % $1.ord }
|
392
|
+
str = str.gsub(/([^\w\,\-.\/\\\: ])/) { "%%%02x".freeze % $1.ord }
|
393
|
+
str.tr!(' '.freeze, '+'.freeze)
|
394
|
+
str
|
392
395
|
end
|
393
396
|
else # Ruby 1.8
|
394
397
|
def url_escape(str) # :nodoc:
|
@@ -398,6 +401,8 @@ def url_escape(str) # :nodoc:
|
|
398
401
|
|
399
402
|
# Unescapes naughty URL characters.
|
400
403
|
def url_unescape(str) # :nodoc:
|
401
|
-
str.tr('+', ' '
|
404
|
+
str = str.tr('+'.freeze, ' '.freeze)
|
405
|
+
str.gsub!(/%([a-f0-9][a-f0-9])/i) { [$1.to_i(16)].pack('C'.freeze) }
|
406
|
+
str
|
402
407
|
end
|
403
408
|
end
|
data/lib/mogilefs/bigfile.rb
CHANGED
@@ -26,6 +26,7 @@ def bigfile_write(key, wr_io, opts = { :verify => false })
|
|
26
26
|
# we only decode raw zlib deflated streams that mogtool (unfortunately)
|
27
27
|
# generates. tarballs and gzip(1) are up to to the application to decrypt.
|
28
28
|
if info[:compressed] || opts[:verify]
|
29
|
+
require 'mogilefs/bigfile/filter'
|
29
30
|
wr_io = MogileFS::Bigfile::Filter.new(wr_io, info, opts)
|
30
31
|
end
|
31
32
|
|
@@ -85,7 +86,6 @@ def bigfile_parse_info(info) # :nodoc:
|
|
85
86
|
rv
|
86
87
|
end
|
87
88
|
end
|
88
|
-
require "mogilefs/bigfile/filter"
|
89
89
|
|
90
90
|
__END__
|
91
91
|
# Copied from mogtool:
|
@@ -33,7 +33,7 @@ def flush
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def write(buf)
|
36
|
-
|
36
|
+
unless @zi
|
37
37
|
if @info[:compressed] &&
|
38
38
|
INFLATABLE_TYPES.include?(@info[:type]) &&
|
39
39
|
buf.bytesize >= 2 &&
|
@@ -51,7 +51,7 @@ def write(buf)
|
|
51
51
|
if @zi
|
52
52
|
buf = @zi.inflate(buf)
|
53
53
|
else
|
54
|
-
@md5
|
54
|
+
@md5.update(buf) if @md5
|
55
55
|
end
|
56
56
|
@io.write(buf)
|
57
57
|
end
|
data/lib/mogilefs/chunker.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
class MogileFS::Chunker
|
3
|
-
CRLF = "\r\n"
|
4
3
|
attr_reader :io
|
5
4
|
|
6
5
|
def initialize(io, md5, expect_md5)
|
@@ -14,7 +13,7 @@ def write(buf)
|
|
14
13
|
@io.write("#{rv.to_s(16)}\r\n")
|
15
14
|
@io.write(buf)
|
16
15
|
@md5.update(buf) if @md5
|
17
|
-
@io.write(
|
16
|
+
@io.write("\r\n".freeze)
|
18
17
|
rv
|
19
18
|
end
|
20
19
|
|
@@ -30,7 +29,7 @@ def flush
|
|
30
29
|
end
|
31
30
|
@io.write("0\r\nContent-MD5: #{content_md5}\r\n\r\n")
|
32
31
|
else
|
33
|
-
@io.write("0\r\n\r\n")
|
32
|
+
@io.write("0\r\n\r\n".freeze)
|
34
33
|
end
|
35
34
|
end
|
36
35
|
end
|
data/lib/mogilefs/client.rb
CHANGED
@@ -27,6 +27,7 @@ def initialize(args)
|
|
27
27
|
@readonly = args[:readonly] ? true : false
|
28
28
|
@timeout = args[:timeout]
|
29
29
|
@fail_timeout = args[:fail_timeout]
|
30
|
+
@connect_timeout = args[:connect_timeout]
|
30
31
|
|
31
32
|
reload
|
32
33
|
end
|
@@ -37,7 +38,8 @@ def initialize(args)
|
|
37
38
|
def reload
|
38
39
|
@backend = MogileFS::Backend.new(:hosts => @hosts,
|
39
40
|
:timeout => @timeout,
|
40
|
-
:fail_timeout => @fail_timeout
|
41
|
+
:fail_timeout => @fail_timeout,
|
42
|
+
:connect_timeout => @connect_timeout)
|
41
43
|
end
|
42
44
|
|
43
45
|
##
|
data/lib/mogilefs/mogilefs.rb
CHANGED
@@ -64,6 +64,10 @@ class MogileFS::MogileFS < MogileFS::Client
|
|
64
64
|
# Timeout for tracker backend responses.
|
65
65
|
# Defaults to 3 seconds.
|
66
66
|
#
|
67
|
+
# [:connect_timeout => Integer]
|
68
|
+
#
|
69
|
+
# Timeout for connecting to a tracker
|
70
|
+
# Defaults to 3 seconds
|
67
71
|
def initialize(args = {})
|
68
72
|
@domain = args[:domain]
|
69
73
|
|
@@ -503,7 +507,7 @@ def file_info(key, args = nil)
|
|
503
507
|
def file_info_cleanup(rv) # :nodoc:
|
504
508
|
%w(fid length devcount).each { |f| rv[f] = rv[f].to_i }
|
505
509
|
devids = rv["devids"] and
|
506
|
-
rv["devids"] = devids.split(
|
510
|
+
rv["devids"] = devids.split(','.freeze).map! { |x| x.to_i }
|
507
511
|
rv
|
508
512
|
end
|
509
513
|
|
@@ -540,7 +544,7 @@ def file_debug(args)
|
|
540
544
|
nexttry|fromdevid|failcount|flags|devid|type)\z/x
|
541
545
|
rv[k] = v.to_i
|
542
546
|
when /devids\z/
|
543
|
-
rv[k] = v.split(
|
547
|
+
rv[k] = v.split(','.freeze).map! { |x| x.to_i }
|
544
548
|
end
|
545
549
|
end
|
546
550
|
end
|
@@ -5,13 +5,21 @@
|
|
5
5
|
class MogileFS::Socket < Socket
|
6
6
|
include MogileFS::SocketCommon
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
sock.connect_nonblock(sockaddr_in(port, host))
|
12
|
-
|
8
|
+
if RUBY_VERSION.to_f >= 2.3
|
9
|
+
def self.start(host, port)
|
10
|
+
sock = new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
11
|
+
sock.connect_nonblock(sockaddr_in(port, host), :exception => false)
|
12
|
+
sock.post_init(host, port)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
def self.start(host, port)
|
16
|
+
sock = new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
17
|
+
begin
|
18
|
+
sock.connect_nonblock(sockaddr_in(port, host))
|
19
|
+
rescue Errno::EINPROGRESS
|
20
|
+
end
|
21
|
+
sock.post_init(host, port)
|
13
22
|
end
|
14
|
-
sock.post_init(host, port)
|
15
23
|
end
|
16
24
|
|
17
25
|
def self.tcp(host, port, timeout = 5)
|
@@ -23,50 +31,83 @@ def self.tcp(host, port, timeout = 5)
|
|
23
31
|
sock
|
24
32
|
end
|
25
33
|
|
26
|
-
def wait_writable(timeout)
|
34
|
+
def wait_writable(timeout = nil)
|
27
35
|
IO.select(nil, [ self ], nil, timeout)
|
28
36
|
end unless self.instance_methods.include?(:wait_writable) # Ruby <2.0.0
|
29
37
|
|
30
|
-
def timed_read(len, dst = "", timeout = 5)
|
31
|
-
begin
|
32
|
-
wait_readable(timeout) or unreadable_socket!(timeout)
|
33
|
-
return read_nonblock(len, dst)
|
34
|
-
rescue Errno::EAGAIN
|
35
|
-
rescue EOFError
|
36
|
-
return
|
37
|
-
end while true
|
38
|
-
end
|
39
|
-
|
40
38
|
def timed_peek(len, dst, timeout = 5)
|
41
39
|
begin
|
42
|
-
wait_readable(timeout) or unreadable_socket!(timeout)
|
43
40
|
rc = recv_nonblock(len, Socket::MSG_PEEK)
|
44
41
|
return rc.empty? ? nil : dst.replace(rc)
|
45
42
|
rescue Errno::EAGAIN
|
43
|
+
wait(timeout) or unreadable_socket!(timeout)
|
46
44
|
rescue EOFError
|
47
45
|
dst.replace("")
|
48
46
|
return
|
49
47
|
end while true
|
48
|
+
rescue EOFError
|
49
|
+
nil
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
52
|
+
# write_nonblock and read_nonblock support `exception: false`
|
53
|
+
if RUBY_VERSION.to_f >= 2.1
|
54
|
+
def timed_read(len, dst = "", timeout = 5)
|
55
|
+
case rc = read_nonblock(len, dst, :exception => false)
|
56
|
+
when String
|
57
|
+
return rc
|
58
|
+
when :wait_readable
|
59
|
+
wait(timeout) or unreadable_socket!(timeout)
|
60
|
+
when nil
|
61
|
+
return
|
62
|
+
end while true
|
63
|
+
rescue EOFError
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def timed_write(buf, timeout = 5)
|
68
|
+
written = 0
|
69
|
+
expect = buf.bytesize
|
70
|
+
case rc = write_nonblock(buf, :exception => false)
|
71
|
+
when Integer
|
72
|
+
return expect if rc == buf.bytesize
|
73
|
+
written += rc
|
74
|
+
buf = buf.byteslice(rc, buf.bytesize) # Ruby 1.9.3+
|
75
|
+
when :wait_writable
|
76
|
+
wait_writable(timeout) or request_truncated!(written, expect, timeout)
|
77
|
+
end while true
|
78
|
+
end
|
79
|
+
else # Ruby 1.8.7 - 2.0.0
|
80
|
+
def timed_read(len, dst = "", timeout = 5)
|
81
|
+
begin
|
82
|
+
return read_nonblock(len, dst)
|
83
|
+
rescue Errno::EAGAIN
|
84
|
+
wait(timeout) or unreadable_socket!(timeout)
|
85
|
+
rescue EOFError
|
86
|
+
return
|
87
|
+
end while true
|
88
|
+
rescue EOFError
|
89
|
+
nil
|
90
|
+
end
|
59
91
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
92
|
+
def timed_write(buf, timeout = 5)
|
93
|
+
written = 0
|
94
|
+
expect = buf.bytesize
|
95
|
+
begin
|
96
|
+
rc = write_nonblock(buf)
|
97
|
+
return expect if rc == buf.bytesize
|
98
|
+
written += rc
|
99
|
+
|
100
|
+
if buf.respond_to?(:byteslice) # Ruby 1.9.3+
|
101
|
+
buf = buf.byteslice(rc, buf.bytesize)
|
102
|
+
else
|
103
|
+
if buf.respond_to?(:encoding) && buf.encoding != Encoding::BINARY
|
104
|
+
buf = buf.dup.force_encoding(Encoding::BINARY)
|
105
|
+
end
|
106
|
+
buf = buf.slice(rc, buf.bytesize)
|
65
107
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end while true
|
108
|
+
rescue Errno::EAGAIN
|
109
|
+
wait_writable(timeout) or request_truncated!(written, expect, timeout)
|
110
|
+
end while true
|
111
|
+
end
|
71
112
|
end
|
72
113
|
end
|
data/pkg.mk
CHANGED
data/test/fresh.rb
CHANGED
@@ -82,7 +82,7 @@ def add_host_device_domain
|
|
82
82
|
# MogileFS::Server 2.60+ shows reject_bad_md5 monitor status
|
83
83
|
dev = @admin.get_devices[0]
|
84
84
|
if dev.include?("reject_bad_md5")
|
85
|
-
assert [true, false].include?(dev["reject_bad_md5"])
|
85
|
+
assert [true, false, nil].include?(dev["reject_bad_md5"]), dev.inspect
|
86
86
|
end
|
87
87
|
|
88
88
|
out = err = nil
|
data/test/test_backend.rb
CHANGED
@@ -25,12 +25,14 @@ def test_initialize
|
|
25
25
|
|
26
26
|
assert_equal ['localhost:1'], @backend.hosts
|
27
27
|
assert_equal 3, @backend.timeout
|
28
|
+
assert_equal 3, @backend.instance_variable_get(:@connect_timeout)
|
28
29
|
assert_equal nil, @backend.lasterr
|
29
30
|
assert_equal nil, @backend.lasterrstr
|
30
31
|
assert_equal({}, @backend.dead)
|
31
32
|
|
32
33
|
@backend = MogileFS::Backend.new :hosts => ['localhost:6001'], :timeout => 1
|
33
34
|
assert_equal 1, @backend.timeout
|
35
|
+
assert_equal 1, @backend.instance_variable_get(:@connect_timeout)
|
34
36
|
end
|
35
37
|
|
36
38
|
def test_do_request
|
@@ -223,5 +225,18 @@ def test_fail_timeout
|
|
223
225
|
c = MogileFS::MogileFS.new(o)
|
224
226
|
assert_equal 0.666, c.backend.instance_variable_get(:@fail_timeout)
|
225
227
|
end
|
226
|
-
end
|
227
228
|
|
229
|
+
def test_connect_timeout
|
230
|
+
o = {
|
231
|
+
:domain => "none",
|
232
|
+
:hosts => %w(0:666 0:6 0:66),
|
233
|
+
:connect_timeout => 1
|
234
|
+
}
|
235
|
+
c = MogileFS::MogileFS.new(o)
|
236
|
+
assert_equal 1, c.backend.instance_variable_get(:@connect_timeout)
|
237
|
+
o[:timeout] = 5
|
238
|
+
c = MogileFS::MogileFS.new(o)
|
239
|
+
assert_equal 1, c.backend.instance_variable_get(:@connect_timeout)
|
240
|
+
assert_equal 5, c.backend.instance_variable_get(:@timeout)
|
241
|
+
end
|
242
|
+
end
|
data/test/test_fresh.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mogilefs-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- mogilefs-client hackers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: olddoc
|
@@ -36,6 +36,7 @@ extensions: []
|
|
36
36
|
extra_rdoc_files: []
|
37
37
|
files:
|
38
38
|
- ".document"
|
39
|
+
- ".gitattributes"
|
39
40
|
- ".gitignore"
|
40
41
|
- ".manifest"
|
41
42
|
- ".olddoc.yml"
|
@@ -126,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
127
|
version: '0'
|
127
128
|
requirements: []
|
128
129
|
rubyforge_project:
|
129
|
-
rubygems_version: 2.
|
130
|
+
rubygems_version: 2.6.6
|
130
131
|
signing_key:
|
131
132
|
specification_version: 4
|
132
133
|
summary: client - MogileFS client library for Ruby
|