rtfs 0.1.2 → 0.2.0
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/README.md +70 -18
- data/lib/rtfs.rb +0 -3
- data/lib/rtfs/client.rb +188 -6
- data/lib/rtfs/version.rb +2 -2
- data/spec/basics_spec.rb +24 -0
- data/spec/custom_spec.rb +28 -0
- data/spec/spec_helper.rb +6 -1
- metadata +50 -22
- data/.gitignore +0 -3
- data/lib/rtfs/tfstool.rb +0 -89
- data/lib/rtfs/web_service.rb +0 -96
- data/lib/rtfs/wrapper.rb +0 -68
- data/rtfs.gemspec +0 -19
- data/spec/a.jpg +0 -0
- data/spec/b.jpg +0 -0
- data/spec/upload_spec.rb +0 -14
data/README.md
CHANGED
@@ -1,19 +1,30 @@
|
|
1
1
|
RTFS
|
2
2
|
====
|
3
3
|
|
4
|
-
RTFS is a Ruby Client
|
4
|
+
RTFS is a Ruby Client of [TFS](http://code.taobao.org/project/view/366/).
|
5
5
|
|
6
|
-
|
6
|
+
For TFS integration with CarrierWave, see
|
7
|
+
[carrierwave-tfs](http://github.com/huacnlee/carrierwave-tfs) for more
|
8
|
+
information.
|
7
9
|
|
8
|
-
This is basicly library for TFS access, you can use [carrierwave-tfs](http://github.com/huacnlee/carrierwave-tfs) for Carrierwave.
|
9
10
|
|
10
11
|
Requirements
|
11
12
|
------------
|
12
13
|
|
13
|
-
* Linux System (
|
14
|
-
*
|
14
|
+
* <del>Linux System (tfstool can only be built on Linux Systems).</del>
|
15
|
+
* <del>tfstool</del>
|
15
16
|
|
16
|
-
|
17
|
+
2013-03-14
|
18
|
+
|
19
|
+
These are no longer valid requirements. TFS now favors
|
20
|
+
[Web Service API](http://baike.corp.taobao.com/index.php/CS_RD/tfs/use_web_service),
|
21
|
+
which requires nothing but a decent REST client of the language you choose.
|
22
|
+
|
23
|
+
See the [official documentation](http://baike.corp.taobao.com/index.php/CS_RD/tfs)
|
24
|
+
for more information about APIs and service applications.
|
25
|
+
|
26
|
+
|
27
|
+
Install
|
17
28
|
-------
|
18
29
|
|
19
30
|
```bash
|
@@ -23,16 +34,16 @@ $ gem install rtfs
|
|
23
34
|
Configure
|
24
35
|
---------
|
25
36
|
|
26
|
-
|
37
|
+
or How to integrate into Rails app.
|
27
38
|
|
28
|
-
|
39
|
+
Create `tfs.yml` in your rails app's config directory. The content of it should
|
40
|
+
look like this:
|
29
41
|
|
30
42
|
```yaml
|
43
|
+
# config/tfs.yml
|
31
44
|
defaults: &defaults
|
32
|
-
host: '127.0.0.1:3100'
|
33
|
-
# or use WebService
|
34
45
|
host: 'http://127.0.0.1:3900'
|
35
|
-
appkey: "
|
46
|
+
appkey: "myprecious"
|
36
47
|
|
37
48
|
development:
|
38
49
|
<<: *defaults
|
@@ -44,21 +55,24 @@ production:
|
|
44
55
|
<<: *defaults
|
45
56
|
```
|
46
57
|
|
47
|
-
|
58
|
+
Create an initializer too. Call it `tfs.rb` or whatever filename you fancy:
|
59
|
+
|
48
60
|
|
49
61
|
```ruby
|
62
|
+
# config/initializers/tfs.rb
|
50
63
|
require 'rtfs'
|
51
64
|
tfs_config = YAML.load_file("#{Rails.root}/config/tfs.yml")[Rails.env]
|
52
65
|
$tfs = RTFS::Client.tfs(tfs_config.merge({:ns_addr => tfs_config['host']}))
|
53
66
|
```
|
54
67
|
|
55
|
-
|
68
|
+
Then `$tfs` will be available as a global object.
|
56
69
|
|
57
70
|
|
58
|
-
Usage
|
59
|
-
|
71
|
+
Simple Usage
|
72
|
+
------------
|
60
73
|
|
61
74
|
```ruby
|
75
|
+
# app/controllers/users_controller.rb
|
62
76
|
class UsersController < ApplicationController
|
63
77
|
def save
|
64
78
|
@user = User.new
|
@@ -68,7 +82,7 @@ class UsersController < ApplicationController
|
|
68
82
|
end
|
69
83
|
end
|
70
84
|
|
71
|
-
|
85
|
+
# app/models/user.rb
|
72
86
|
class User < ActiveRecord::Base
|
73
87
|
def avatar_url
|
74
88
|
server_id = self.id % 4 + 1
|
@@ -77,9 +91,47 @@ class User < ActiveRecord::Base
|
|
77
91
|
end
|
78
92
|
```
|
79
93
|
|
80
|
-
Put local file
|
94
|
+
Put local file into TFS server:
|
81
95
|
|
82
96
|
```bash
|
97
|
+
$ bundle exec rails c
|
83
98
|
irb> $tfs.put("~/Downloads/a.jpg")
|
84
99
|
T1Ub1XXeFBXXb1upjX
|
85
|
-
RTFS
|
100
|
+
RTFS
|
101
|
+
```
|
102
|
+
|
103
|
+
|
104
|
+
API
|
105
|
+
---
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
# initialize client.
|
109
|
+
tfs = RTFS::Client.new(:ns_addr => 'http://127.0.0.1:3800',
|
110
|
+
:appkey => 'myprecious',
|
111
|
+
:basedir => Rails.root.join('public'))
|
112
|
+
|
113
|
+
# simply put file.
|
114
|
+
tfs.put('foo.jpg') # ==> T1Ub1XXeFBXXb1upjX
|
115
|
+
|
116
|
+
# stat file.
|
117
|
+
tfs.stat('T1Ub1XXeFBXXb1upjX')
|
118
|
+
|
119
|
+
# remove file.
|
120
|
+
tfs.rm('T1Ub1XXeFBXXb1upjX')
|
121
|
+
|
122
|
+
# put named file, keep that name in TFS.
|
123
|
+
tfs.save('foo.jpg') # ==> 133/7463/foo.jpg
|
124
|
+
|
125
|
+
# remove named file.
|
126
|
+
tfs.del('foo.jpg') # ==> true
|
127
|
+
|
128
|
+
# put files under public folder, and preserve its name.
|
129
|
+
# will prepend the basedir in front of the file path passed in.
|
130
|
+
tfs.save('jquery.js')
|
131
|
+
```
|
132
|
+
|
133
|
+
|
134
|
+
Executabes?
|
135
|
+
-----------
|
136
|
+
|
137
|
+
RTFS will be available as a executable binary soon. Stay tuned...
|
data/lib/rtfs.rb
CHANGED
@@ -10,13 +10,10 @@ def require_local(suffix)
|
|
10
10
|
end
|
11
11
|
|
12
12
|
require 'rubygems'
|
13
|
-
require 'nice-ffi'
|
14
13
|
require 'yaml'
|
15
14
|
|
16
15
|
# require_local 'rtfs/wrapper.rb'
|
17
16
|
require 'rtfs/client.rb'
|
18
|
-
require 'rtfs/web_service.rb'
|
19
|
-
require 'rtfs/tfstool.rb'
|
20
17
|
require 'rtfs/meta.rb'
|
21
18
|
require 'rtfs/version.rb'
|
22
19
|
|
data/lib/rtfs/client.rb
CHANGED
@@ -1,21 +1,203 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
# Code for client
|
3
3
|
# by nowa<nowazhu@gmail.com> 2011-07-23
|
4
|
+
require 'rest-client'
|
5
|
+
require 'open-uri'
|
6
|
+
require 'digest'
|
7
|
+
require 'json'
|
8
|
+
|
4
9
|
module RTFS
|
5
10
|
|
6
11
|
class Client
|
7
12
|
|
8
13
|
# 参数:
|
9
|
-
# :
|
10
|
-
# :tfstool_path tfstool 安装路径,默认 /home/admin/tfs/bin/tfstool
|
14
|
+
# :root TFS WebService Root 服务器地址,如 127.0.0.1:3100
|
11
15
|
def self.tfs(options)
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
self.new(options) if options
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :nameservers
|
20
|
+
attr_accessor :appkey
|
21
|
+
attr_accessor :uid
|
22
|
+
|
23
|
+
def initialize(options)
|
24
|
+
# 通过 ns_addr 的地址获取负载均衡的地址
|
25
|
+
@nameservers = open("#{options[:ns_addr]}/tfs.list").read.split("\n")
|
26
|
+
@appkey = options[:appkey]
|
27
|
+
|
28
|
+
@basedir = options[:basedir]
|
29
|
+
@uid = options[:uid]
|
30
|
+
|
31
|
+
if @uid.nil? && !@basedir.nil?
|
32
|
+
(Digest::MD5.hexdigest(@basedir).to_i(16) % 10000)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# 获取文件
|
37
|
+
def get(tfs_name)
|
38
|
+
http_get("/v1/#{appkey}/#{tfs_name}")
|
39
|
+
end
|
40
|
+
|
41
|
+
# 上传文件
|
42
|
+
# 参数:
|
43
|
+
# file_path 需要上传的文件路径
|
44
|
+
# :ext 扩展名,默认会取 file_path 的扩展名, 如: .jpg
|
45
|
+
# 返回值
|
46
|
+
# T1lpVcXftHXXaCwpjX
|
47
|
+
def put(path, options = {})
|
48
|
+
ext = options[:ext] || File.extname(path)
|
49
|
+
path = path.to_s
|
50
|
+
resp = http_post("/v1/#{appkey}",
|
51
|
+
File.open(path.start_with?('/') ? path : fpath(path)).read,
|
52
|
+
:params => {
|
53
|
+
:suffix => ext,
|
54
|
+
:simple_name => options[:simple_name] || 0
|
55
|
+
},
|
56
|
+
:accept => :json)
|
57
|
+
|
58
|
+
json = JSON.parse(resp)
|
59
|
+
json && json['TFS_FILE_NAME']
|
60
|
+
end
|
61
|
+
|
62
|
+
# 上传文件 并返回完整 url (only for Taobao)
|
63
|
+
def put_and_get_url(path, options = {})
|
64
|
+
ext = options[:ext] || File.extname(path)
|
65
|
+
path = path.to_s
|
66
|
+
tname = put(path, :ext => ext)
|
67
|
+
|
68
|
+
"http://img0#{rand(4)+1}.taobaocdn.com/tfscom/#{t}#{ext}" unless tname.nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
# 删除文件, 不能带扩展名
|
72
|
+
def rm(tname, options = {})
|
73
|
+
resp = http_delete("/v1/#{appkey}/#{tname}", :params => options)
|
74
|
+
|
75
|
+
resp && resp.code == 200
|
76
|
+
end
|
77
|
+
|
78
|
+
# 文件信息查看, 不能带扩展名
|
79
|
+
def stat(tname, options = {})
|
80
|
+
resp = http_get("/v1/#{appkey}/metadata/#{tname}", :params => options)
|
81
|
+
|
82
|
+
if resp && resp.code == 200
|
83
|
+
JSON.parse(resp)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def save(path, options = {})
|
88
|
+
path = path.to_s
|
89
|
+
|
90
|
+
if finger(path)
|
91
|
+
del(path)
|
92
|
+
save(path)
|
93
|
+
elsif create(path)
|
94
|
+
write(path)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def create(path, options = {})
|
99
|
+
resp = http_post(furl(path), nil,
|
100
|
+
:params => {:recursive => 1})
|
101
|
+
|
102
|
+
resp && resp.code == 201
|
103
|
+
end
|
104
|
+
|
105
|
+
def write(path, options = {})
|
106
|
+
data = File.open(fpath(path)).read
|
107
|
+
return if data.length == 0
|
108
|
+
resp = http_put(furl(path), data,
|
109
|
+
:params => {:offset => 0})
|
110
|
+
|
111
|
+
if resp && resp.code == 200
|
112
|
+
[appid, fuid(path), path].join('/')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def del(path, options = {})
|
117
|
+
resp = http_delete(furl(path))
|
118
|
+
|
119
|
+
resp && resp.code == 200
|
120
|
+
end
|
121
|
+
|
122
|
+
def finger(path, options = {})
|
123
|
+
begin
|
124
|
+
resp = http_head(furl(path))
|
125
|
+
|
126
|
+
resp && resp.code == 200
|
127
|
+
rescue RestClient::ResourceNotFound
|
128
|
+
# this rescue is intended.
|
129
|
+
# the purpose is to return nil if the path fingered returned 404.
|
130
|
+
# hence there's nothing more to do.
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def appid
|
135
|
+
return @appid unless @appid.nil?
|
136
|
+
|
137
|
+
resp = http_get("/v2/#{appkey}/appid")
|
138
|
+
|
139
|
+
if resp && resp.code == 200
|
140
|
+
@appid = JSON.parse(resp)['APP_ID']
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def http_get(url, *args)
|
148
|
+
RestClient.get("#{nameserver}#{url}", *args)
|
149
|
+
end
|
150
|
+
|
151
|
+
def http_post(url, *args)
|
152
|
+
RestClient.post("#{nameserver}#{url}", *args)
|
153
|
+
end
|
154
|
+
|
155
|
+
def http_delete(url, *args)
|
156
|
+
RestClient.delete("#{nameserver}#{url}", *args)
|
157
|
+
end
|
158
|
+
|
159
|
+
def http_put(url, *args)
|
160
|
+
RestClient.put("#{nameserver}#{url}", *args)
|
161
|
+
end
|
162
|
+
|
163
|
+
def http_head(url, *args)
|
164
|
+
RestClient.head("#{nameserver}#{url}", *args)
|
165
|
+
end
|
166
|
+
|
167
|
+
# 随机取一个 nameserver 用于访问,tfs.list 内容如下:
|
168
|
+
#
|
169
|
+
# 50
|
170
|
+
# 10.246.65.133:3900
|
171
|
+
# 10.246.65.132:3900
|
172
|
+
# 10.246.65.131:3900
|
173
|
+
# 10.246.65.130:3900
|
174
|
+
# 10.246.73.70:3900
|
175
|
+
# 10.246.73.71:3900
|
176
|
+
# 10.246.73.72:3900
|
177
|
+
#
|
178
|
+
def nameserver
|
179
|
+
# 现在第一行是 50,表示频次,访问 50 次就要更新
|
180
|
+
@nameservers[rand(@nameservers.size - 1) + 1]
|
181
|
+
end
|
182
|
+
|
183
|
+
# TFS 中需要这个 User ID,用来分散数据请求,减轻单台机器压力,最方便 uid 获取方式是,
|
184
|
+
# 对传入的文件路径做哈希,取后几位。但是如果要把 TFS 当普通 CDN 使用,
|
185
|
+
# 能够使用确定的 URL 更新文件的话,User ID 必须固定,只能不定期批量更新。
|
186
|
+
# 因此这里也允许使用实例化时传入的 uid 参数。
|
187
|
+
def fuid(path = nil)
|
188
|
+
if @uid.nil? && path.nil?
|
189
|
+
1
|
15
190
|
else
|
16
|
-
|
191
|
+
@uid || Digest::MD5.hexdigest(path).to_i(16) % 10000
|
17
192
|
end
|
18
193
|
end
|
19
194
|
|
195
|
+
def furl(path)
|
196
|
+
"/v2/#{appkey}/#{appid}/#{fuid(path)}/file/#{path}"
|
197
|
+
end
|
198
|
+
|
199
|
+
def fpath(path)
|
200
|
+
@basedir ? File.join(@basedir, path) : path
|
201
|
+
end
|
20
202
|
end
|
21
203
|
end
|
data/lib/rtfs/version.rb
CHANGED
data/spec/basics_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
describe 'Basic operations' do
|
5
|
+
it 'should put and remove file' do
|
6
|
+
fname = $tfs.put('spec/a.jpg')
|
7
|
+
|
8
|
+
f = open("http://img01.daily.taobaocdn.net/tfscom/#{fname}.jpg")
|
9
|
+
f.read.length.should eq(6511)
|
10
|
+
|
11
|
+
$tfs.rm(fname).should eq(true)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should stat file' do
|
15
|
+
fname = $tfs.put('spec/a.jpg')
|
16
|
+
stat = $tfs.stat(fname)
|
17
|
+
|
18
|
+
stat.should.is_a? Hash
|
19
|
+
stat['FILE_NAME'].should eq(fname)
|
20
|
+
stat['SIZE'].should eq(6511)
|
21
|
+
|
22
|
+
$tfs.rm(fname)
|
23
|
+
end
|
24
|
+
end
|
data/spec/custom_spec.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
describe 'custom operations' do
|
5
|
+
it 'should save named file' do
|
6
|
+
path = $tfs.save('spec/a.jpg')
|
7
|
+
|
8
|
+
f = open("http://img01.daily.taobaocdn.net/L1/#{path}")
|
9
|
+
f.read.length.should eq(6511)
|
10
|
+
|
11
|
+
$tfs.del('spec/a.jpg').should eq(true)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should expose appid' do
|
15
|
+
$tfs.appid.should eq('133')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should respect global uid settings' do
|
19
|
+
# 自动生成的 uid 是经过 % 10000 的,也就是说,不会超过 10000,
|
20
|
+
# 所以,自定义 uid 只要大于等于 10000,就尽管用吧
|
21
|
+
$tfs.uid = 10000
|
22
|
+
path = $tfs.save('spec/a.jpg')
|
23
|
+
|
24
|
+
path.should =~ /10000/
|
25
|
+
|
26
|
+
# $tfs.del('spec/a.jpg').should eq(true)
|
27
|
+
end
|
28
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
1
|
require "rtfs"
|
2
2
|
|
3
|
-
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.color = true
|
5
|
+
end
|
6
|
+
|
7
|
+
$tfs = RTFS::Client.tfs(:ns_addr => 'http://10.232.4.44:3800',
|
8
|
+
:appkey => '4f8fbb734d4d8')
|
metadata
CHANGED
@@ -1,26 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rtfs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Nowa Zhu
|
9
9
|
- Jason Lee
|
10
10
|
- Cricy
|
11
|
+
- Jake Chen
|
11
12
|
autorequire:
|
12
13
|
bindir: bin
|
13
14
|
cert_chain: []
|
14
|
-
date:
|
15
|
+
date: 2013-03-15 00:00:00.000000000 Z
|
15
16
|
dependencies:
|
16
17
|
- !ruby/object:Gem::Dependency
|
17
|
-
name:
|
18
|
+
name: rest-client
|
18
19
|
requirement: !ruby/object:Gem::Requirement
|
19
20
|
none: false
|
20
21
|
requirements:
|
21
22
|
- - ! '>='
|
22
23
|
- !ruby/object:Gem::Version
|
23
|
-
version:
|
24
|
+
version: 1.6.0
|
24
25
|
type: :runtime
|
25
26
|
prerelease: false
|
26
27
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -28,7 +29,23 @@ dependencies:
|
|
28
29
|
requirements:
|
29
30
|
- - ! '>='
|
30
31
|
- !ruby/object:Gem::Version
|
31
|
-
version:
|
32
|
+
version: 1.6.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: json
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
type: :development
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
32
49
|
- !ruby/object:Gem::Dependency
|
33
50
|
name: rest-client
|
34
51
|
requirement: !ruby/object:Gem::Requirement
|
@@ -37,7 +54,7 @@ dependencies:
|
|
37
54
|
- - ! '>='
|
38
55
|
- !ruby/object:Gem::Version
|
39
56
|
version: 1.6.0
|
40
|
-
type: :
|
57
|
+
type: :development
|
41
58
|
prerelease: false
|
42
59
|
version_requirements: !ruby/object:Gem::Requirement
|
43
60
|
none: false
|
@@ -45,11 +62,28 @@ dependencies:
|
|
45
62
|
- - ! '>='
|
46
63
|
- !ruby/object:Gem::Version
|
47
64
|
version: 1.6.0
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: rspec
|
67
|
+
requirement: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ~>
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '2.5'
|
73
|
+
type: :development
|
74
|
+
prerelease: false
|
75
|
+
version_requirements: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ~>
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '2.5'
|
48
81
|
description:
|
49
82
|
email:
|
50
83
|
- nowazhu@gmail.com
|
51
84
|
- huacnlee@gmail.com
|
52
85
|
- feiyelanghai@gmail.com
|
86
|
+
- jakeplus@gmail.com
|
53
87
|
executables: []
|
54
88
|
extensions: []
|
55
89
|
extra_rdoc_files:
|
@@ -57,24 +91,19 @@ extra_rdoc_files:
|
|
57
91
|
- CHANGES.md
|
58
92
|
- MIT-LICENSE
|
59
93
|
files:
|
60
|
-
- .gitignore
|
61
|
-
- CHANGES.md
|
62
|
-
- MIT-LICENSE
|
63
|
-
- README.md
|
64
|
-
- lib/rtfs.rb
|
65
94
|
- lib/rtfs/client.rb
|
66
95
|
- lib/rtfs/meta.rb
|
67
|
-
- lib/rtfs/tfstool.rb
|
68
96
|
- lib/rtfs/version.rb
|
69
|
-
- lib/rtfs
|
70
|
-
-
|
71
|
-
-
|
72
|
-
-
|
73
|
-
- spec/
|
97
|
+
- lib/rtfs.rb
|
98
|
+
- README.md
|
99
|
+
- CHANGES.md
|
100
|
+
- MIT-LICENSE
|
101
|
+
- spec/basics_spec.rb
|
102
|
+
- spec/custom_spec.rb
|
74
103
|
- spec/spec_helper.rb
|
75
|
-
- spec/upload_spec.rb
|
76
104
|
homepage: https://github.com/nowa/rtfs
|
77
|
-
licenses:
|
105
|
+
licenses:
|
106
|
+
- MIT
|
78
107
|
post_install_message:
|
79
108
|
rdoc_options: []
|
80
109
|
require_paths:
|
@@ -98,7 +127,6 @@ signing_key:
|
|
98
127
|
specification_version: 3
|
99
128
|
summary: RTFS is a Ruby Client for TFS.
|
100
129
|
test_files:
|
101
|
-
- spec/
|
102
|
-
- spec/
|
130
|
+
- spec/basics_spec.rb
|
131
|
+
- spec/custom_spec.rb
|
103
132
|
- spec/spec_helper.rb
|
104
|
-
- spec/upload_spec.rb
|
data/.gitignore
DELETED
data/lib/rtfs/tfstool.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
module RTFS
|
3
|
-
|
4
|
-
TFSTOOL_PATH = "/home/admin/tfs/bin/tfstool"
|
5
|
-
class Tfstool
|
6
|
-
|
7
|
-
attr_accessor :ns_addr
|
8
|
-
attr_accessor :tfstool_path
|
9
|
-
|
10
|
-
# 参数:
|
11
|
-
# :ns_addr TFS 服务器地址,如 127.0.0.1:3100
|
12
|
-
# :tfstool_path tfstool 安装路径,默认 /home/admin/tfs/bin/tfstool
|
13
|
-
def initialize(options)
|
14
|
-
return nil unless options
|
15
|
-
@ns_addr = options[:ns_addr]
|
16
|
-
@tfstool_path = options[:tfstool_path] ? options[:tfstool_path] : TFSTOOL_PATH
|
17
|
-
if not File.exist?(@tfstool_path)
|
18
|
-
puts "[RTFS] debuger: #{@tfstool_path}"
|
19
|
-
raise NoTFSToolError.new
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
# 获取文件
|
24
|
-
def get(tfs_name, local_file=nil)
|
25
|
-
local_file ||= tfs_name
|
26
|
-
`#{tfstool_cmd} -i "get #{tfs_name} #{local_file}"`
|
27
|
-
end
|
28
|
-
|
29
|
-
# 上传文件
|
30
|
-
# 参数:
|
31
|
-
# file_path 需要上传的文件路径
|
32
|
-
# :ext 扩展名,默认会取 file_path 的扩展名, 如: .jpg
|
33
|
-
# 返回值
|
34
|
-
# T1lpVcXftHXXaCwpjX
|
35
|
-
def put(file_path, options = {})
|
36
|
-
ext = options[:ext] || File.extname(file_path)
|
37
|
-
tfs_name = "NULL"
|
38
|
-
result = `#{tfstool_cmd} -i "put #{file_path} #{tfs_name} #{ext}"`
|
39
|
-
# puts "result: #{result}"
|
40
|
-
t = nil
|
41
|
-
if result.include?("=> ")
|
42
|
-
result = result.split("=> ").last
|
43
|
-
if result.include?(",")
|
44
|
-
t = result.split(",").first
|
45
|
-
end
|
46
|
-
end
|
47
|
-
t.nil? ? nil : t
|
48
|
-
end
|
49
|
-
|
50
|
-
# 上传文件 并返回完整 url (only for Taobao)
|
51
|
-
def put_and_get_url(file_path, options = {})
|
52
|
-
ext = options[:ext] || File.extname(file_path)
|
53
|
-
t = put(file_path, :ext => ext)
|
54
|
-
t.nil? ? nil : "http://img03.taobaocdn.com/tfscom/#{t}#{ext}"
|
55
|
-
end
|
56
|
-
|
57
|
-
# 删除文件, 不能带扩展名
|
58
|
-
def rm(tfs_name)
|
59
|
-
`#{tfstool_cmd} -i "rm #{tfs_name}"`
|
60
|
-
end
|
61
|
-
|
62
|
-
# 改名, 不能带扩展名
|
63
|
-
def rename(tfs_name, new_name)
|
64
|
-
`#{tfstool_cmd} -i "rename #{tfs_name} #{new_name}"`
|
65
|
-
end
|
66
|
-
|
67
|
-
# 文件信息查看, 不能带扩展名
|
68
|
-
def stat(tfs_name)
|
69
|
-
result = `#{tfstool_cmd} -i "stat #{tfs_name}"`
|
70
|
-
stat = {}
|
71
|
-
result.split("\n").each do |line|
|
72
|
-
line = line.split(':').map {|i| i.gsub!(" ", "")}
|
73
|
-
stat["#{line.first}"] = line.last
|
74
|
-
end
|
75
|
-
stat
|
76
|
-
end
|
77
|
-
|
78
|
-
protected
|
79
|
-
def tfstool_cmd
|
80
|
-
"#{@tfstool_path} -n -s #{@ns_addr}"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
class NoTFSToolError < RuntimeError
|
85
|
-
def to_s
|
86
|
-
"You must install tfs from yum or source first!"
|
87
|
-
end
|
88
|
-
end # NoTFSToolError
|
89
|
-
end
|
data/lib/rtfs/web_service.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
require 'rest-client'
|
3
|
-
require 'open-uri'
|
4
|
-
require "json"
|
5
|
-
|
6
|
-
module RTFS
|
7
|
-
class WebService
|
8
|
-
|
9
|
-
attr_accessor :appkey
|
10
|
-
attr_accessor :ns_addrs
|
11
|
-
|
12
|
-
def initialize(options)
|
13
|
-
return nil unless options
|
14
|
-
# 通过 ns_addr 的地址获取负载均衡的地址
|
15
|
-
@ns_addrs ||= open("#{options[:ns_addr]}/tfs.list").read.split("\n")
|
16
|
-
@appkey = options[:appkey]
|
17
|
-
end
|
18
|
-
|
19
|
-
# 随机取一个 ns_addr 用于访问
|
20
|
-
# 50
|
21
|
-
# 10.246.65.133:3900
|
22
|
-
# 10.246.65.132:3900
|
23
|
-
# 10.246.65.131:3900
|
24
|
-
# 10.246.65.130:3900
|
25
|
-
# 10.246.73.70:3900
|
26
|
-
# 10.246.73.71:3900
|
27
|
-
# 10.246.73.72:3900
|
28
|
-
def ns_addr
|
29
|
-
|
30
|
-
#现在第一行是50,表示频次,访问50次就要更新
|
31
|
-
@ns_addrs[rand(@ns_addrs.size - 1) + 1]
|
32
|
-
end
|
33
|
-
|
34
|
-
# 获取文件
|
35
|
-
def get(tfs_name, local_file=nil)
|
36
|
-
RestClient.get(get_url("/v1/#{appkey}/#{tfs_name}"))
|
37
|
-
end
|
38
|
-
|
39
|
-
# 上传文件
|
40
|
-
# 参数:
|
41
|
-
# file_path 需要上传的文件路径
|
42
|
-
# :ext 扩展名,默认会取 file_path 的扩展名, 如: .jpg
|
43
|
-
# 返回值
|
44
|
-
# T1lpVcXftHXXaCwpjX
|
45
|
-
def put(file_path, options = {})
|
46
|
-
ext = options[:ext] || File.extname(file_path)
|
47
|
-
response = RestClient.post(get_url("/v1/#{appkey}",{:suffix => ext, :simple_name => options[:simple_name] || 0}),File.open(file_path).read, :accept => :json )
|
48
|
-
json = JSON.parse(response)
|
49
|
-
json && json["TFS_FILE_NAME"] ? json["TFS_FILE_NAME"] : nil
|
50
|
-
end
|
51
|
-
|
52
|
-
# 上传文件 并返回完整 url (only for Taobao)
|
53
|
-
def put_and_get_url(file_path, options = {})
|
54
|
-
ext = options[:ext] || File.extname(file_path)
|
55
|
-
t = put(file_path, :ext => ext)
|
56
|
-
t.nil? ? nil : "http://img0#{rand(4)+1}.taobaocdn.com/tfscom/#{t}#{ext}"
|
57
|
-
end
|
58
|
-
|
59
|
-
# 删除文件, 不能带扩展名
|
60
|
-
def rm(tfs_name,options = {})
|
61
|
-
response = RestClient.delete(get_url("/v1/#{appkey}/#{tfs_name}", options))
|
62
|
-
response && response.code == 200 ? true : nil
|
63
|
-
end
|
64
|
-
|
65
|
-
# 改名, 不能带扩展名
|
66
|
-
def rename(tfs_name, new_name)
|
67
|
-
#`#{tfstool_cmd} -i "rename #{tfs_name} #{new_name}"`
|
68
|
-
end
|
69
|
-
|
70
|
-
# 文件信息查看, 不能带扩展名
|
71
|
-
def stat(tfs_name,options = {})
|
72
|
-
response = RestClient.get(get_url("/v1/#{appkey}/metadata/#{tfs_name}", options))
|
73
|
-
response && response.code == 200 ? JSON.parse(response) : nil
|
74
|
-
end
|
75
|
-
|
76
|
-
def client_name
|
77
|
-
"WebService"
|
78
|
-
end
|
79
|
-
|
80
|
-
private
|
81
|
-
|
82
|
-
def appkey
|
83
|
-
@appkey
|
84
|
-
end
|
85
|
-
|
86
|
-
def get_url(url,opts = {})
|
87
|
-
return url unless opts
|
88
|
-
url = "#{self.ns_addr}#{url}"
|
89
|
-
params = []
|
90
|
-
opts.each{ |k,v|
|
91
|
-
params << "#{k}=#{v}"
|
92
|
-
}
|
93
|
-
url.include?("?") ? "#{url}&#{params.join("&")}" : "#{url}?#{params.join("&")}"
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
data/lib/rtfs/wrapper.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
# 使用ffi和nice-ffi来调用动态库的c接口
|
3
|
-
# tfsclient c api目前尚不完善,基于c接口的封装调用不能满足需求
|
4
|
-
# 因此目前不使用该wrapper,文件暂存
|
5
|
-
# by nowa<nowazhu@gmail.com> 2011-07-23
|
6
|
-
|
7
|
-
module LibC
|
8
|
-
extend FFI::Library
|
9
|
-
# figures out the correct libc for each platform including Windows
|
10
|
-
library = ffi_lib(FFI::Library::LIBC).first
|
11
|
-
|
12
|
-
# Size_t not working properly on Windows
|
13
|
-
find_type(:size_t) rescue typedef(:ulong, :size_t)
|
14
|
-
|
15
|
-
# memory allocators
|
16
|
-
attach_function :malloc, [:size_t], :pointer
|
17
|
-
attach_function :free, [:pointer], :void
|
18
|
-
|
19
|
-
# get a pointer to the free function; used for ZMQ::Message deallocation
|
20
|
-
Free = library.find_symbol('free')
|
21
|
-
|
22
|
-
# memory movers
|
23
|
-
attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer
|
24
|
-
end # module LibC
|
25
|
-
|
26
|
-
module LibRTFS
|
27
|
-
extend NiceFFI::Library
|
28
|
-
|
29
|
-
libs_dir = "/home/admin/tfs/lib/"
|
30
|
-
pathset = NiceFFI::PathSet::DEFAULT.prepend(libs_dir)
|
31
|
-
|
32
|
-
load_library("tfsclient_c", pathset)
|
33
|
-
|
34
|
-
attach_function :t_initialize, [:string, :int, :int], :int
|
35
|
-
|
36
|
-
#enum OpenFlag
|
37
|
-
# {
|
38
|
-
# T_READ = 1,
|
39
|
-
# T_WRITE = 2,
|
40
|
-
# T_CREATE = 4,
|
41
|
-
# T_NEWBLK = 8,
|
42
|
-
# T_NOLEASE = 16,
|
43
|
-
# T_STAT = 32,
|
44
|
-
# T_LARGE = 64
|
45
|
-
# };
|
46
|
-
attach_function :t_open, [:string, :string, :string, :int, :string], :int
|
47
|
-
attach_function :t_read, [:int, :pointer, :int], :int
|
48
|
-
attach_function :t_write, [:int, :pointer, :int], :int
|
49
|
-
attach_function :t_lseek, [:int, :int, :int], :int
|
50
|
-
attach_function :t_pread, [:int, :pointer, :int, :int], :int
|
51
|
-
attach_function :t_pwrite, [:int, :pointer, :int, :int], :int
|
52
|
-
attach_function :t_fstat, [:int, :pointer, :int], :int
|
53
|
-
attach_function :t_close, [:int, :string, :int], :int
|
54
|
-
attach_function :t_unlink, [:string, :string, :int], :int
|
55
|
-
end
|
56
|
-
|
57
|
-
# 下面这部分代码仅供测试LibRTFS
|
58
|
-
# LibRTFS.t_initialize("10.232.35.32:3100", 5, 1000)
|
59
|
-
# fd = LibRTFS.t_open("test", ".png", nil, 2, nil)
|
60
|
-
# puts "fd: #{fd}"
|
61
|
-
#
|
62
|
-
# ori_file = nil
|
63
|
-
# File.open("/home/xifeng/1.png", "r") do |f|
|
64
|
-
# ori_file = f.read
|
65
|
-
# end
|
66
|
-
# buffer = LibC.malloc ori_file.size
|
67
|
-
# buffer.write_string ori_file, ori_file.size
|
68
|
-
# puts "write: #{LibRTFS.t_write(fd, buffer, ori_file.size)}"
|
data/rtfs.gemspec
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
Gem::Specification.new do |s|
|
3
|
-
s.name = "rtfs"
|
4
|
-
s.version = "0.1.2"
|
5
|
-
s.platform = Gem::Platform::RUBY
|
6
|
-
s.has_rdoc = true
|
7
|
-
s.extra_rdoc_files = ["README.md","CHANGES.md","MIT-LICENSE",]
|
8
|
-
s.summary = "RTFS is a Ruby Client for TFS."
|
9
|
-
s.author = ["Nowa Zhu","Jason Lee","Cricy"]
|
10
|
-
s.email = ["nowazhu@gmail.com","huacnlee@gmail.com","feiyelanghai@gmail.com"]
|
11
|
-
s.homepage = "https://github.com/nowa/rtfs"
|
12
|
-
s.files = `git ls-files`.split("\n")
|
13
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
-
s.require_paths = ["lib"]
|
15
|
-
s.bindir = 'bin'
|
16
|
-
|
17
|
-
s.add_dependency("nice-ffi", ">=0.4")
|
18
|
-
s.add_dependency("rest-client", ">=1.6.0")
|
19
|
-
end
|
data/spec/a.jpg
DELETED
Binary file
|
data/spec/b.jpg
DELETED
Binary file
|
data/spec/upload_spec.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require "open-uri"
|
3
|
-
|
4
|
-
describe "Base upload" do
|
5
|
-
it "should upload" do
|
6
|
-
fname = $tfs.put("spec/a.jpg")
|
7
|
-
f = open("http://10.232.4.42/tfscom/#{fname}.jpg")
|
8
|
-
f.read.length.should == 6153
|
9
|
-
|
10
|
-
fname = $tfs.put("spec/b.jpg")
|
11
|
-
f = open("http://10.232.4.42/tfscom/#{fname}.jpg")
|
12
|
-
f.read.length.should == 114504
|
13
|
-
end
|
14
|
-
end
|