fastdfs-client 0.0.3 → 0.0.5
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +18 -2
- data/lib/fastdfs-client/cmd.rb +3 -0
- data/lib/fastdfs-client/proto_common.rb +9 -1
- data/lib/fastdfs-client/socket.rb +43 -16
- data/lib/fastdfs-client/storage.rb +98 -13
- data/lib/fastdfs-client/tracker.rb +8 -9
- data/lib/fastdfs-client/utils.rb +4 -1
- data/lib/fastdfs-client/version.rb +1 -1
- data/spec/mock_tcp_socket.rb +23 -1
- data/spec/storage_spec.rb +44 -27
- data/spec/test_config.rb +7 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83f837406bfbb7c59a4da740450c49232800e481
|
4
|
+
data.tar.gz: 900430e39aeea9db93faf42c460394eeb9087277
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 106f6c4cc8406215c7fe22d4935e4b72538f8e7f70874c1f66e129eac483669f8d2d1493bb09f15b2a2d2389fa8e34ffdc21ba5b3944361b53c35836838f303d
|
7
|
+
data.tar.gz: a12327a458920d7c1b4eccdf4828c1a7dc24d3ad0a7bd3d58d108b22fa7932d0cc050d1b0d3dc03b6a9bde3022dd33b349be9245383f18681f786ebc8ed06794
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -10,12 +10,28 @@ fastdfs client for ruby
|
|
10
10
|
### Using
|
11
11
|
|
12
12
|
```RUBY
|
13
|
+
|
14
|
+
# return the result format
|
15
|
+
# {status: true, err_msg: "", result: ...}
|
16
|
+
#
|
17
|
+
|
18
|
+
|
13
19
|
tracker = new Fastdfs::Client::Tracker("192.168.1.1", "22122")
|
14
20
|
|
15
21
|
@storage = tracker.get_storage
|
16
22
|
|
17
23
|
@storage.upload(@file)
|
24
|
+
#result: {group_name: "group1", path: "m1/xfsd/fds.jpg"}
|
25
|
+
|
26
|
+
@storage.delete(path, group_name)
|
27
|
+
|
28
|
+
# flag params [cover, merge]
|
29
|
+
@storage.set_metadata(path, group_name, {author: "kaka", width: "300"}, flag)
|
30
|
+
|
31
|
+
@storage.get_metadata(path, group_name)
|
32
|
+
#result: {author: "kaka", width: "300"}
|
33
|
+
|
34
|
+
@storage.download(path, group_name)
|
35
|
+
#result: #<Tempfile:/var/folders/m7/bt2j0rk54x555t44dpn4b7bm0000gn/T/test.jpg20160416-43738-1560vq3>
|
18
36
|
|
19
|
-
#group_name + path
|
20
|
-
@storage.delete(path)
|
21
37
|
```
|
data/lib/fastdfs-client/cmd.rb
CHANGED
@@ -11,8 +11,16 @@ module Fastdfs
|
|
11
11
|
EXTNAME_LEN = 6
|
12
12
|
GROUP_NAME_MAX_LEN = 16
|
13
13
|
|
14
|
+
RECV_MAX_LEN = 2 * 1024
|
15
|
+
|
16
|
+
RECORD_SEPERATOR = "\u0001"
|
17
|
+
FILE_SEPERATOR = "\u0002"
|
18
|
+
|
19
|
+
SET_METADATA_FLAG_OVERWRITE = "O"
|
20
|
+
SET_METADATA_FLAG_MERGE = "M"
|
21
|
+
|
14
22
|
def self.header_bytes(cmd, hex_long, erron=0)
|
15
|
-
hex_bytes = Utils.
|
23
|
+
hex_bytes = Utils.number_to_buffer(hex_long)
|
16
24
|
header = hex_bytes.fill(0, hex_bytes.length...HEAD_LEN)
|
17
25
|
header[8] = cmd
|
18
26
|
header[9] = erron
|
@@ -10,13 +10,12 @@ module Fastdfs
|
|
10
10
|
attr_accessor :header, :content, :header_len, :cmd, :socket, :host, :port
|
11
11
|
|
12
12
|
def initialize(host, port, options = {})
|
13
|
-
@host = host
|
14
|
-
@port = port
|
15
|
-
connection
|
13
|
+
@host, @port = host, port
|
16
14
|
@header_len = ProtoCommon::HEAD_LEN
|
17
15
|
@options = options || {}
|
18
16
|
@connection_timeout = @options[:connection_timeout] || 3
|
19
|
-
@recv_timeout = @options[:recv_timeout] ||
|
17
|
+
@recv_timeout = @options[:recv_timeout] || 20
|
18
|
+
connection
|
20
19
|
end
|
21
20
|
|
22
21
|
def write(*args)
|
@@ -42,26 +41,54 @@ module Fastdfs
|
|
42
41
|
!@socket.closed?
|
43
42
|
end
|
44
43
|
|
45
|
-
def receive
|
46
|
-
|
47
|
-
Timeout.timeout(@recv_timeout) do
|
44
|
+
def receive(&block)
|
45
|
+
timeout_recv do
|
48
46
|
@header = @socket.recv(@header_len).unpack("C*")
|
49
47
|
end
|
50
48
|
res_header = parseHeader
|
51
|
-
if res_header[:
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
if res_header[:status]
|
50
|
+
recv_body if is_recv?
|
51
|
+
res = yield(@content) if block_given?
|
52
|
+
res_header[:result] = res unless res.nil?
|
55
53
|
end
|
56
|
-
|
54
|
+
res_header
|
57
55
|
end
|
58
56
|
|
59
57
|
private
|
60
58
|
def parseHeader
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
{
|
59
|
+
err_msg = ""
|
60
|
+
err_msg = "recv package size #{@header} is not equal #{@header_len}, cmd: #{@cmd}" unless @header.length == @header_len
|
61
|
+
err_msg = "recv cmd: #{@header[8]} is not correct, expect cmd: #{CMD::RESP_CODE}, cmd: #{@cmd}" unless @header[8] == CMD::RESP_CODE
|
62
|
+
err_msg = "recv erron #{@header[9]}, 0 is correct cmd: #{@cmd}" unless @header[9] == 0
|
63
|
+
{status: err_msg.blank?, err_msg: err_msg}
|
64
|
+
end
|
65
|
+
|
66
|
+
def timeout_recv
|
67
|
+
Timeout.timeout(@recv_timeout) do
|
68
|
+
yield if block_given?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def is_recv?
|
73
|
+
recv_body_len > 0
|
74
|
+
end
|
75
|
+
|
76
|
+
def recv_body_len
|
77
|
+
@header[0...8].to_pack_long
|
78
|
+
end
|
79
|
+
|
80
|
+
def recv_body
|
81
|
+
@content = ""
|
82
|
+
max_len, body_len = ProtoCommon::RECV_MAX_LEN, recv_body_len
|
83
|
+
|
84
|
+
while body_len > 0
|
85
|
+
timeout_recv do
|
86
|
+
len = [body_len, max_len].min
|
87
|
+
@content << @socket.recv(len)
|
88
|
+
body_len -= len
|
89
|
+
end
|
90
|
+
end
|
91
|
+
@content = nil if @content.blank?
|
65
92
|
end
|
66
93
|
|
67
94
|
end
|
@@ -6,8 +6,8 @@ module Fastdfs
|
|
6
6
|
class Storage
|
7
7
|
extend Hook
|
8
8
|
|
9
|
-
before(:upload, :delete){ @socket.connection }
|
10
|
-
after(:upload, :delete){ @socket.close }
|
9
|
+
before(:upload, :delete, :get_metadata, ){ @socket.connection }
|
10
|
+
after(:upload, :delete, :get_metadata, ){ @socket.close }
|
11
11
|
|
12
12
|
attr_accessor :host, :port, :group_name, :store_path, :socket, :options
|
13
13
|
|
@@ -28,26 +28,89 @@ module Fastdfs
|
|
28
28
|
|
29
29
|
def delete(path, group_name = nil)
|
30
30
|
cmd = CMD::DELETE_FILE
|
31
|
+
path_bytes = header_path_bytes(cmd, path, group_name)
|
32
|
+
@socket.write(cmd, path_bytes)
|
33
|
+
@socket.receive{ true }
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_metadata(path, group_name = nil)
|
37
|
+
cmd = CMD::GET_METADATA
|
38
|
+
path_bytes = header_path_bytes(cmd, path, group_name)
|
39
|
+
@socket.write(cmd, path_bytes)
|
40
|
+
@socket.receive do |content|
|
41
|
+
res = content.split(ProtoCommon::RECORD_SEPERATOR).map do |c|
|
42
|
+
c.split(ProtoCommon::FILE_SEPERATOR)
|
43
|
+
end.flatten
|
44
|
+
Utils.symbolize_keys(Hash[*res])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_metadata(path, group_name = nil, options = {}, flag = :cover)
|
49
|
+
cmd = CMD::SET_METADATA
|
50
|
+
|
51
|
+
unless options.is_a?(Hash)
|
52
|
+
flag = options
|
53
|
+
options = {}
|
54
|
+
end
|
55
|
+
|
56
|
+
if group_name.is_a?(Hash)
|
57
|
+
options = group_name
|
58
|
+
group_name = nil
|
59
|
+
end
|
60
|
+
flag = convert_meta_flag(flag)
|
61
|
+
group_bytes, path_bytes = group_path_bytes(path, group_name)
|
62
|
+
meta_bytes = meta_to_bytes(options)
|
63
|
+
|
64
|
+
size_bytes = Utils.number_to_buffer(path_bytes.length) + Utils.number_to_buffer(meta_bytes.length)
|
65
|
+
size_bytes = (size_bytes).fill(0, size_bytes.length...16)
|
66
|
+
total = size_bytes.length + flag.length + group_bytes.length + path_bytes.length + meta_bytes.length
|
67
|
+
header_bytes = ProtoCommon.header_bytes(cmd, total)
|
68
|
+
@socket.write(cmd, (header_bytes + size_bytes + flag.bytes + group_bytes + path_bytes))
|
69
|
+
@socket.write(cmd, meta_bytes)
|
70
|
+
@socket.receive
|
71
|
+
end
|
72
|
+
|
73
|
+
def download(path, group_name = nil)
|
74
|
+
cmd = CMD::DOWNLOAD_FILE
|
75
|
+
group_bytes, path_bytes = group_path_bytes(path, group_name)
|
76
|
+
download_bytes = Utils.number_to_buffer(0) + Utils.number_to_buffer(0)
|
77
|
+
|
78
|
+
header_bytes = ProtoCommon.header_bytes(cmd, group_bytes.length + path_bytes.length + download_bytes.length)
|
79
|
+
|
80
|
+
@socket.write(cmd, header_bytes + download_bytes + group_bytes + path_bytes)
|
81
|
+
@socket.receive do |body|
|
82
|
+
create_tempfile(path, body) if body
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
def group_path_bytes(path, group_name = nil)
|
88
|
+
group_name, path = extract_path!(path, group_name)
|
89
|
+
group_bytes = group_name.bytes.fill(0, group_name.length...ProtoCommon::GROUP_NAME_MAX_LEN)
|
90
|
+
[group_bytes, path.bytes]
|
91
|
+
end
|
92
|
+
|
93
|
+
def header_path_bytes(cmd, path, group_name = nil)
|
94
|
+
path_bytes = group_path_bytes(path, group_name).flatten
|
95
|
+
return (ProtoCommon.header_bytes(cmd, path_bytes.length) + path_bytes)
|
96
|
+
end
|
97
|
+
|
98
|
+
def extract_path!(path, group_name = nil)
|
31
99
|
raise "path arguments is empty!" if path.blank?
|
32
100
|
if group_name.blank?
|
33
101
|
group_name = /^\/?(\w+)/.match(path)[1]
|
34
102
|
path = path.gsub(Regexp.new("/?#{group_name}/?"), "")
|
35
103
|
end
|
36
104
|
raise "group_name arguments is empty!" if group_name.blank?
|
37
|
-
|
38
|
-
path_length = (group_bytes.length + path.bytes.length)
|
39
|
-
|
40
|
-
@socket.write(cmd, (ProtoCommon.header_bytes(cmd, path_length) + group_bytes + path.bytes))
|
41
|
-
@socket.receive{ true }
|
105
|
+
return group_name, path
|
42
106
|
end
|
43
107
|
|
44
|
-
private
|
45
108
|
def _upload(file)
|
46
109
|
cmd = CMD::UPLOAD_FILE
|
47
110
|
|
48
111
|
extname = File.extname(file)[1..-1]
|
49
112
|
ext_name_bs = extname.bytes.fill(0, extname.length...@extname_len)
|
50
|
-
hex_len_bytes = Utils.
|
113
|
+
hex_len_bytes = Utils.number_to_buffer(file.size)
|
51
114
|
size_byte = [@store_path].concat(hex_len_bytes).fill(0, (hex_len_bytes.length+1)...@size_len)
|
52
115
|
|
53
116
|
header = ProtoCommon.header_bytes(cmd, (size_byte.length + @extname_len + file.size))
|
@@ -58,12 +121,34 @@ module Fastdfs
|
|
58
121
|
@socket.receive do |body|
|
59
122
|
group_name_max_len = ProtoCommon::GROUP_NAME_MAX_LEN
|
60
123
|
|
61
|
-
{
|
62
|
-
group_name: body[0...group_name_max_len].strip,
|
63
|
-
path: body[group_name_max_len..-1]
|
64
|
-
}
|
124
|
+
{group_name: body[0...group_name_max_len].strip, path: body[group_name_max_len..-1]}
|
65
125
|
end
|
66
126
|
end
|
127
|
+
|
128
|
+
def convert_meta_flag(flag)
|
129
|
+
data = {
|
130
|
+
cover: ProtoCommon::SET_METADATA_FLAG_OVERWRITE,
|
131
|
+
merge: ProtoCommon::SET_METADATA_FLAG_MERGE
|
132
|
+
}
|
133
|
+
flag = :cover if flag.blank?
|
134
|
+
data[flag.to_sym]
|
135
|
+
end
|
136
|
+
|
137
|
+
def meta_to_bytes(options = {})
|
138
|
+
meta_bytes = options.map do |a|
|
139
|
+
a.join(ProtoCommon::FILE_SEPERATOR)
|
140
|
+
end.join(ProtoCommon::RECORD_SEPERATOR).bytes
|
141
|
+
meta_bytes << 0 if meta_bytes.blank?
|
142
|
+
meta_bytes
|
143
|
+
end
|
144
|
+
|
145
|
+
def create_tempfile(path, body)
|
146
|
+
tmp = Tempfile.new(path.gsub(/\//, "_"))
|
147
|
+
tmp.binmode
|
148
|
+
tmp.write(body)
|
149
|
+
tmp.close
|
150
|
+
tmp
|
151
|
+
end
|
67
152
|
|
68
153
|
end
|
69
154
|
|
@@ -19,15 +19,14 @@ module Fastdfs
|
|
19
19
|
def get_storage
|
20
20
|
header = ProtoCommon.header_bytes(@cmd, 0)
|
21
21
|
@socket.write(@cmd, header)
|
22
|
-
@socket.receive
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
22
|
+
res = @socket.receive
|
23
|
+
return res unless res[:status]
|
24
|
+
|
25
|
+
storage_ip = @socket.content[ProtoCommon::IPADDR].strip
|
26
|
+
storage_port = @socket.content[ProtoCommon::PORT].unpack("C*").to_pack_long
|
27
|
+
store_path = @socket.content[ProtoCommon::TRACKER_BODY_LEN-1].unpack("C*")[0]
|
28
|
+
|
29
|
+
Storage.new(storage_ip, storage_port, store_path, options)
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
data/lib/fastdfs-client/utils.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Utils
|
2
2
|
|
3
|
-
def self.
|
3
|
+
def self.number_to_buffer(num)
|
4
4
|
8.times.map{|i| (num >> (56 - 8 * i)) & 255}
|
5
5
|
end
|
6
6
|
|
@@ -10,4 +10,7 @@ module Utils
|
|
10
10
|
arr1
|
11
11
|
end
|
12
12
|
|
13
|
+
def self.symbolize_keys(obj)
|
14
|
+
obj.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
15
|
+
end
|
13
16
|
end
|
data/spec/mock_tcp_socket.rb
CHANGED
@@ -45,7 +45,7 @@ class TCPSocket
|
|
45
45
|
|
46
46
|
group_name = Utils.array_merge([].fill(0, 0...16), TestConfig::GROUP_NAME.bytes)
|
47
47
|
ip = Utils.array_merge([].fill(0, 0...15), TestConfig::STORAGE_IP.bytes)
|
48
|
-
port = Utils.
|
48
|
+
port = Utils.number_to_buffer(TestConfig::STORAGE_PORT.to_i)
|
49
49
|
store_path = Array(TestConfig::STORE_PATH)
|
50
50
|
|
51
51
|
(header+group_name+ip+port+store_path)[@recv_offset...@recv_offset+len].pack("C*")
|
@@ -68,6 +68,28 @@ class TCPSocket
|
|
68
68
|
header = ProtoCommon.header_bytes(CMD::RESP_CODE, 0)
|
69
69
|
header.pack("C*")
|
70
70
|
end
|
71
|
+
},
|
72
|
+
"15" => {
|
73
|
+
recv_bytes: lambda do |len|
|
74
|
+
header = ProtoCommon.header_bytes(CMD::RESP_CODE, 0)
|
75
|
+
body = TestConfig::METADATA.map{|a| a.join(ProtoCommon::FILE_SEPERATOR)}.join(ProtoCommon::RECORD_SEPERATOR).bytes
|
76
|
+
header[7] = body.length
|
77
|
+
(header + body)[@recv_offset...@recv_offset+len].pack("C*")
|
78
|
+
end
|
79
|
+
},
|
80
|
+
"13" => {
|
81
|
+
recv_bytes: lambda do |len|
|
82
|
+
header = ProtoCommon.header_bytes(CMD::RESP_CODE, 0)
|
83
|
+
header.pack("C*")
|
84
|
+
end
|
85
|
+
},
|
86
|
+
"14" => {
|
87
|
+
recv_bytes: lambda do |len|
|
88
|
+
header = ProtoCommon.header_bytes(CMD::RESP_CODE, 0)
|
89
|
+
body = IO.read(TestConfig::FILE).bytes
|
90
|
+
header[7] = body.length
|
91
|
+
(header + body)[@recv_offset...@recv_offset+len].pack("C*")
|
92
|
+
end
|
71
93
|
}
|
72
94
|
}
|
73
95
|
end
|
data/spec/storage_spec.rb
CHANGED
@@ -8,33 +8,50 @@ describe Fastdfs::Client::Storage do
|
|
8
8
|
let(:tracker){ FC::Tracker.new(host, port) }
|
9
9
|
let(:storage){ tracker.get_storage }
|
10
10
|
|
11
|
-
it "initialize the server" do
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
it "should have access to the storage connection" do
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
it "should the result attributes group_name and path" do
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
11
|
+
# it "initialize the server" do
|
12
|
+
# expect(FC::Socket).to receive(:new).with(host, port, nil)
|
13
|
+
# FC::Storage.new(host, port)
|
14
|
+
# end
|
15
|
+
|
16
|
+
# it "should have access to the storage connection" do
|
17
|
+
# expect(storage.socket).to receive(:connection)
|
18
|
+
# expect(storage.socket).to receive(:close)
|
19
|
+
# storage.upload(TestConfig::FILE)
|
20
|
+
# end
|
21
|
+
|
22
|
+
# it "should the result attributes group_name and path" do
|
23
|
+
# res = storage.upload(TestConfig::FILE)
|
24
|
+
# expect(res[:status]).to be_truthy
|
25
|
+
# expect(res[:result]).to include(:group_name)
|
26
|
+
# expect(res[:result]).to include(:path)
|
27
|
+
# end
|
28
|
+
|
29
|
+
# it "can delete file by group and path" do
|
30
|
+
# res = storage.upload(TestConfig::FILE)[:result]
|
31
|
+
# storage.delete(res[:path], res[:group_name])
|
32
|
+
# end
|
33
|
+
|
34
|
+
# it "can delete file raise exception" do
|
35
|
+
# res = storage.upload(TestConfig::FILE)[:result]
|
36
|
+
# result = FC::ProtoCommon.header_bytes(FC::CMD::RESP_CODE, 0, 22)
|
37
|
+
# TCPSocket.any_instance.stub("recv").and_return(result.pack("C*"))
|
38
|
+
# expect( storage.delete("fdsaf", res[:group_name])[:status] ).to be_falsey
|
39
|
+
# end
|
40
|
+
|
41
|
+
# it "can get metadata results" do
|
42
|
+
# res = storage.get_metadata("#{TestConfig::GROUP_NAME}/#{TestConfig::FILE_NAME}")
|
43
|
+
# expect(res[:result]).to eq(TestConfig::METADATA)
|
44
|
+
# end
|
45
|
+
|
46
|
+
# it "can set metadata" do
|
47
|
+
# expect(storage.set_metadata(TestConfig::FILE_NAME, TestConfig::GROUP_NAME, TestConfig::METADATA)).to be_truthy
|
48
|
+
# end
|
49
|
+
|
50
|
+
it "download the file to the local" do
|
51
|
+
res = storage.download("M00/04/46/wKgIF1b7XLWAI2Q6AAACVHeY6n8655.png", "group1")
|
52
|
+
expect(res[:status]).to be_truthy
|
53
|
+
expect(res[:result]).to be_an_instance_of(Tempfile)
|
54
|
+
expect(IO.read(res[:result])).to eq(IO.read(TestConfig::FILE))
|
32
55
|
|
33
|
-
it "can delete file raise exception" do
|
34
|
-
res = storage.upload(TestConfig::FILE)
|
35
|
-
result = FC::ProtoCommon.header_bytes(FC::CMD::RESP_CODE, 0, 22)
|
36
|
-
TCPSocket.any_instance.stub("recv").and_return(result.pack("C*"))
|
37
|
-
expect{ storage.delete("fdsaf", res[:group_name]) }.to raise_error(RuntimeError)
|
38
56
|
end
|
39
|
-
|
40
57
|
end
|
data/spec/test_config.rb
CHANGED
@@ -5,6 +5,13 @@ module TestConfig
|
|
5
5
|
GROUP_NAME = "group1"
|
6
6
|
FILE_NAME = "M00/04/47/wKgIF1cHcQyAeAF7AAACVHeY6n8267.png"
|
7
7
|
|
8
|
+
METADATA = {
|
9
|
+
width: "800",
|
10
|
+
height: "600",
|
11
|
+
bgcolor: 'red',
|
12
|
+
author: "kaka"
|
13
|
+
}
|
14
|
+
|
8
15
|
FILE = Tempfile.new("test.jpg")
|
9
16
|
FILE.write("testtest")
|
10
17
|
FILE.close
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastdfs-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ka Ka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-04-
|
11
|
+
date: 2016-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -31,6 +31,7 @@ executables: []
|
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
|
+
- ".gitignore"
|
34
35
|
- Gemfile
|
35
36
|
- Gemfile.lock
|
36
37
|
- README.md
|