rtfs 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|