cos 0.1.0 → 0.1.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +13 -2
  5. data/Gemfile +4 -1
  6. data/LICENSE +191 -0
  7. data/README.md +2014 -17
  8. data/Rakefile +23 -6
  9. data/bin/cos +325 -0
  10. data/bin/setup +1 -3
  11. data/cos.gemspec +24 -13
  12. data/lib/cos.rb +41 -4
  13. data/lib/cos/api.rb +289 -0
  14. data/lib/cos/bucket.rb +731 -0
  15. data/lib/cos/checkpoint.rb +62 -0
  16. data/lib/cos/client.rb +58 -0
  17. data/lib/cos/config.rb +102 -0
  18. data/lib/cos/dir.rb +301 -0
  19. data/lib/cos/download.rb +252 -0
  20. data/lib/cos/exception.rb +62 -0
  21. data/lib/cos/file.rb +152 -0
  22. data/lib/cos/http.rb +95 -0
  23. data/lib/cos/logging.rb +47 -0
  24. data/lib/cos/resource.rb +201 -0
  25. data/lib/cos/signature.rb +119 -0
  26. data/lib/cos/slice.rb +292 -0
  27. data/lib/cos/struct.rb +49 -0
  28. data/lib/cos/tree.rb +165 -0
  29. data/lib/cos/util.rb +82 -0
  30. data/lib/cos/version.rb +2 -2
  31. data/spec/cos/bucket_spec.rb +562 -0
  32. data/spec/cos/client_spec.rb +77 -0
  33. data/spec/cos/dir_spec.rb +195 -0
  34. data/spec/cos/download_spec.rb +105 -0
  35. data/spec/cos/http_spec.rb +70 -0
  36. data/spec/cos/signature_spec.rb +83 -0
  37. data/spec/cos/slice_spec.rb +302 -0
  38. data/spec/cos/struct_spec.rb +38 -0
  39. data/spec/cos/tree_spec.rb +322 -0
  40. data/spec/cos/util_spec.rb +106 -0
  41. data/test/download_test.rb +44 -0
  42. data/test/list_test.rb +43 -0
  43. data/test/upload_test.rb +48 -0
  44. metadata +132 -21
  45. data/.idea/.name +0 -1
  46. data/.idea/cos.iml +0 -49
  47. data/.idea/encodings.xml +0 -6
  48. data/.idea/misc.xml +0 -14
  49. data/.idea/modules.xml +0 -8
  50. data/.idea/workspace.xml +0 -465
  51. data/bin/console +0 -14
data/Rakefile CHANGED
@@ -1,10 +1,27 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ #!/usr/bin/env rake
3
2
 
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList['test/**/*_test.rb']
3
+ require 'bundler'
4
+ require 'bundler/gem_tasks'
5
+ require 'rspec/core/rake_task'
6
+
7
+ # Rspec 单元测试
8
+ RSpec::Core::RakeTask.new(:spec) do
9
+ Bundler.setup(:default, :test)
10
+ end
11
+
12
+ # 运行示例
13
+ task :example do
14
+ FileList['example/**/*.rb'].each do |f|
15
+ puts "==== Run example: #{f} ===="
16
+ ruby f
17
+ end
18
+ end
19
+
20
+ require 'rake/testtask'
21
+
22
+ # 集成测试
23
+ Rake::TestTask.new do |t|
24
+ t.pattern = 'test/**/*_test.rb'
8
25
  end
9
26
 
10
27
  task :default => :spec
data/bin/cos ADDED
@@ -0,0 +1,325 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'bundler/setup'
5
+ require 'thor'
6
+ require 'yaml'
7
+ require 'cos'
8
+
9
+ class COS_CLI < Thor
10
+
11
+ DEFAULT_CONFIG = '~/.cos.yml'
12
+
13
+ package_name 'COS Ruby SDK CLI'
14
+
15
+ class_option :config, aliases: '-c', default: DEFAULT_CONFIG, desc: '加载配置文件'
16
+ class_option :bucket, aliases: '-b', desc: '指定Bucket'
17
+
18
+ desc 'list [PATH]', '获取目录列表'
19
+ method_option :prefix, aliases: '-p', desc: '前缀搜索'
20
+ method_option :num, aliases: '-n', desc: '每页拉取的数量'
21
+ method_option :pattern, aliases: '-t', desc: '获取方式', banner: 'file, dir, both'
22
+ method_option :order, aliases: '-o', desc: '排序方式', banner: 'asc, desc'
23
+ def list(path = '')
24
+ rescue_errors do
25
+ bucket.list(path, enabled_options([:prefix, :num, :pattern, :order])).each do |res|
26
+ if res.type == 'file'
27
+ puts(res.path, :blue)
28
+ else
29
+ puts(res.path)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ map ls: :list
36
+
37
+ desc 'create_folder [PATH]', '创建目录'
38
+ method_option :biz_attr, aliases: '-r', desc: '业务属性'
39
+ def create_folder(path)
40
+ rescue_errors do
41
+ dir = bucket.create_folder(path, enabled_options([:biz_attr]))
42
+ puts("#{dir.path} 目录创建成功!", :purple)
43
+ end
44
+ end
45
+
46
+ map mkdir: :create_folder
47
+
48
+ desc 'stat [PATH]', '获取目录或文件信息'
49
+ def stat(path = '')
50
+ rescue_errors do
51
+ stat = bucket.stat(path)
52
+ format_puts_stat(stat.to_hash.to_s)
53
+ end
54
+ end
55
+
56
+ desc 'tree [PATH]', '显示树形结构'
57
+ method_option :depth, aliases: '-d', desc: '目录深度', type: :numeric, default: 5
58
+ method_option :files_count, aliases: '-f', desc: '显示文件个数', type: :boolean, default: false
59
+ method_option :files, aliases: '-a', desc: '列出文件', type: :boolean, default: false
60
+ def tree(path = '')
61
+ rescue_errors do
62
+ path_obj = bucket.stat(path)
63
+ COS::Tree.new(
64
+ enabled_options([:depth, :files_count, :files]).merge({path: path_obj})
65
+ ).print_tree
66
+ end
67
+ end
68
+
69
+ desc 'upload [PATH] [FILE_NAME] [FILE_SRC]', '上传文件(大文件自动分片上传,支持多线程上传,断点续传)'
70
+ method_option :biz_attr, aliases: '-r', desc: '业务属性'
71
+ method_option :min_slice_size, aliases: '-m', desc: '最小完整上传大小', type: :numeric, banner: 'bytes'
72
+ method_option :auto_create_folder, aliases: '-f', desc: '自动创建目录', type: :boolean, default: false
73
+ method_option :disable_cpt, aliases: '-d', desc: '禁用断点续传(分片上传时有效)', type: :boolean, default: false
74
+ method_option :threads, aliases: '-t', desc: '线程数(分片上传时有效)', type: :numeric
75
+ method_option :upload_retry, aliases: '-n', desc: '重试次数(分片上传时有效)', type: :numeric
76
+ method_option :slice_size, aliases: '-s', desc: '分片上传时每个分片的大小(分片上传时有效)', type: :numeric
77
+ method_option :cpt_file, aliases: '-e', desc: '指定断点续传记录(分片上传时有效)'
78
+ def upload(path, file_name, file_src)
79
+ rescue_errors do
80
+ file = bucket.upload(path, file_name, file_src,
81
+ enabled_options([:biz_attr, :min_slice_size, :auto_create_folder, :disable_cpt, :threads, :upload_retry, :slice_size, :cpt_file])) do |percent|
82
+ puts("上传进度: #{(percent*100).round(2)}%", :green)
83
+ end
84
+ format_puts_stat(file.to_hash.to_s)
85
+ puts("#{file_src} 上传完成!", :purple)
86
+ end
87
+ end
88
+
89
+ desc 'upload_all [PATH] [FILE_SRC_PATH]', '上传目录下的所有文件(不含子目录)'
90
+ method_option :biz_attr, aliases: '-r', desc: '业务属性'
91
+ method_option :skip_error, aliases: '-k', desc: '跳过错误', type: :boolean, default: false
92
+ method_option :min_slice_size, aliases: '-m', desc: '最小完整上传大小', type: :numeric, banner: 'bytes'
93
+ method_option :auto_create_folder, aliases: '-f', desc: '自动创建目录', type: :boolean, default: false
94
+ method_option :disable_cpt, aliases: '-d', desc: '禁用断点续传(分片上传时有效)', type: :boolean, default: false
95
+ method_option :threads, aliases: '-t', desc: '线程数(分片上传时有效)', type: :numeric
96
+ method_option :upload_retry, aliases: '-n', desc: '重试次数(分片上传时有效)', type: :numeric
97
+ method_option :slice_size, aliases: '-s', desc: '分片上传时每个分片的大小(分片上传时有效)', type: :numeric
98
+ method_option :cpt_file, aliases: '-e', desc: '指定断点续传记录(分片上传时有效)'
99
+ def upload_all(path, file_src_path)
100
+ rescue_errors do
101
+ files = bucket.upload_all(path, file_src_path,
102
+ enabled_options([:biz_attr, :skip_error, :min_slice_size, :auto_create_folder, :disable_cpt, :threads, :upload_retry, :slice_size, :cpt_file])) do |percent|
103
+ puts("上传进度: #{(percent*100).round(2)}%", :green)
104
+ end
105
+
106
+ files.each do |file|
107
+ puts(file.url, :blue)
108
+ end
109
+ puts("目录: #{file_src_path} 共#{files.count}个文件 上传完成!", :purple)
110
+ end
111
+ end
112
+
113
+ desc 'download [PATH] [FILE_STORE]', '下载文件(大文件自动分片下载,支持多线程下载,断点续传)'
114
+ method_option :min_slice_size, aliases: '-m', desc: '最小完整下载大小', type: :numeric, banner: 'bytes'
115
+ method_option :disable_cpt, aliases: '-d', desc: '禁用断点续传(分片下载时有效)', type: :boolean, default: false
116
+ method_option :threads, aliases: '-t', desc: '线程数(分片下载时有效)', type: :numeric
117
+ method_option :download_retry, aliases: '-n', desc: '重试次数(分片下载时有效)', type: :numeric
118
+ method_option :slice_size, aliases: '-s', desc: '分片下载时每个分片的大小(分片下载时有效)', type: :numeric
119
+ method_option :cpt_file, aliases: '-e', desc: '指定断点续传记录(分片下载时有效)'
120
+ def download(path, file_store)
121
+ rescue_errors do
122
+ file = bucket.download(path, file_store,
123
+ enabled_options([:min_slice_size, :disable_cpt, :threads, :download_retry, :slice_size, :cpt_file])) do |percent|
124
+ puts("下载进度: #{(percent*100).round(2)}%", :green)
125
+ end
126
+ puts(file, :blue)
127
+ puts("#{file_store} 下载完成!", :purple)
128
+ end
129
+ end
130
+
131
+ desc 'download_all [PATH] [FILE_STORE_PATH]', '下载目录下的所有文件(不含子目录)'
132
+ method_option :min_slice_size, aliases: '-m', desc: '最小完整下载大小', type: :numeric, banner: 'bytes'
133
+ method_option :disable_mkdir, aliases: '-k', desc: '禁止自动创建本地目录', type: :boolean, default: false
134
+ method_option :disable_cpt, aliases: '-d', desc: '禁用断点续传(分片下载时有效)', type: :boolean, default: false
135
+ method_option :threads, aliases: '-t', desc: '线程数(分片下载时有效)', type: :numeric
136
+ method_option :download_retry, aliases: '-n', desc: '重试次数(分片下载时有效)', type: :numeric
137
+ method_option :slice_size, aliases: '-s', desc: '分片下载时每个分片的大小(分片下载时有效)', type: :numeric
138
+ method_option :cpt_file, aliases: '-e', desc: '指定断点续传记录(分片下载时有效)'
139
+ def download_all(path, file_store_path)
140
+ rescue_errors do
141
+ files = bucket.download_all(path, file_store_path,
142
+ enabled_options([:min_slice_size, :disable_mkdir, :disable_cpt, :threads, :download_retry, :slice_size, :cpt_file])) do |percent|
143
+ puts("下载进度: #{(percent*100).round(2)}%", :green)
144
+ end
145
+
146
+ files.each do |file|
147
+ puts(file, :blue)
148
+ end
149
+ puts("目录: #{path} 共#{files.count}个文件 下载完成!", :purple)
150
+ end
151
+ end
152
+
153
+ desc 'update [PATH] [BIZ_ATTR]', '更新业务属性'
154
+ def update(path, biz_attr)
155
+ rescue_errors do
156
+ bucket.update(path, biz_attr)
157
+ puts("#{path} 更新成功!", :purple)
158
+ end
159
+ end
160
+
161
+ desc 'delete [PATH]', '删除目录或文件'
162
+ def delete(path)
163
+ rescue_errors do
164
+ bucket.delete(path)
165
+ puts("#{path} 删除成功!", :purple)
166
+ end
167
+ end
168
+
169
+ desc 'url [PATH]', '获取文件的访问URL'
170
+ method_option :cname, aliases: '-e', desc: '使用CNAME', banner: 'cname.domain.com'
171
+ method_option :https, aliases: '-s', desc: '使用HTTPS', type: :boolean
172
+ method_option :expire_seconds, aliases: '-p', desc: '签名有效秒数(对私有空间有效)', type: :numeric
173
+ def url(path)
174
+ rescue_errors do
175
+ puts(bucket.url(path, enabled_options([:cname, :https, :expire_seconds])), :blue)
176
+ end
177
+ end
178
+
179
+ desc 'count [PATH]', '获取文件及目录数'
180
+ def count(path)
181
+ rescue_errors do
182
+ puts(bucket.count(path), :blue)
183
+ end
184
+ end
185
+
186
+ map size: :count
187
+
188
+ desc 'count_files [PATH]', '获取文件数'
189
+ def count_files(path)
190
+ rescue_errors do
191
+ puts(bucket.count_files(path), :blue)
192
+ end
193
+ end
194
+
195
+ desc 'count_dirs [PATH]', '获取目录数'
196
+ def count_dirs(path)
197
+ rescue_errors do
198
+ puts(bucket.count_dirs(path), :blue)
199
+ end
200
+ end
201
+
202
+ desc 'is_exist [PATH]', '判断文件或目录是否存在'
203
+ def is_exist(path)
204
+ rescue_errors do
205
+ puts(bucket.exist?(path), :blue)
206
+ end
207
+ end
208
+
209
+ map exists: :exist
210
+
211
+ desc 'is_empty [PATH]', '判断目录是否为空'
212
+ def is_empty(path)
213
+ rescue_errors do
214
+ puts(bucket.empty?(path), :blue)
215
+ end
216
+ end
217
+
218
+ desc 'is_complete [PATH]', '判断文件是否上传完整'
219
+ def is_complete(path)
220
+ rescue_errors do
221
+ puts(bucket.complete?(path), :blue)
222
+ end
223
+ end
224
+
225
+ desc 'sign_once [PATH]', '生成单次可用签名'
226
+ def sign_once(path)
227
+ rescue_errors do
228
+ puts(bucket.client.signature.once(bucket.bucket_name, path), :blue)
229
+ end
230
+ end
231
+
232
+ desc 'sign_multi [EXPIRE]', '生成多次可用签名'
233
+ def sign_multi(expire)
234
+ puts(bucket.client.signature.once(bucket.bucket_name, expire), :blue)
235
+ end
236
+
237
+ desc 'init', '创建默认配置文件'
238
+ method_option :file, aliases: '-f', desc: '指定创建配置文件的路径', default: DEFAULT_CONFIG
239
+ def init
240
+ if File.exist?(File.expand_path(options[:file]))
241
+ puts('文件已存在!', :red)
242
+ exit!
243
+ else
244
+ yml = {
245
+ 'app_id' => 'your_app_id',
246
+ 'secret_id' => 'your_secret_id',
247
+ 'secret_key' => 'your_secret_key',
248
+ 'default_bucket' => 'your_default_bucket'
249
+ }
250
+
251
+ File.open(File.expand_path(options[:file]), 'w') do |f|
252
+ f.write(yml.to_yaml)
253
+ end
254
+
255
+ puts("默认配置文件已创建在 #{options[:file]}", :purple)
256
+ end
257
+ end
258
+
259
+ desc 'help [COMMAND]', '获取指令的使用帮助'
260
+ def help(*args)
261
+ super(*args)
262
+ end
263
+
264
+ private
265
+
266
+ def enabled_options(hash_array)
267
+ ep = Hash.new
268
+ hash_array.each do |key|
269
+ ep[key] = options[key]
270
+ end
271
+ ep
272
+ end
273
+
274
+ def format_puts_stat(stat)
275
+ # 格式化hash字符串
276
+ stat.gsub!('{:', "{\n :")
277
+ stat.gsub!('=>', ' => ')
278
+ stat.gsub!(', :', ",\n :")
279
+ stat.gsub!('}', "\n}")
280
+ puts(stat, :blue)
281
+ end
282
+
283
+ def rescue_errors(&block)
284
+ block.call if block
285
+ rescue => error
286
+ puts("#{error}", :red)
287
+ exit!
288
+ end
289
+
290
+ def bucket
291
+ # 判断配置文件是否存在
292
+ unless File.exist?(File.expand_path(options[:config]))
293
+ puts('未找到配置文件, 使用 [cos init] 指令创建默认配置文件', :red)
294
+ exit!
295
+ end
296
+
297
+ COS::Logging::set_logger(STDOUT, Logger::INFO)
298
+ COS.client(config: options[:config]).bucket(options[:bucket])
299
+ end
300
+
301
+ end
302
+
303
+ # 打印输出着色
304
+ class Object
305
+ def puts(message, color = nil)
306
+ color_table = {
307
+ :red => '31;1',
308
+ :green => '32;1',
309
+ :yellow => '33;1',
310
+ :blue => '34;1',
311
+ :purple => '35;1',
312
+ :sky => '36;1',
313
+ }
314
+
315
+ if color and color_table.has_key?(color.to_sym)
316
+ print "\e[#{color_table[color]}m"
317
+ Kernel::puts message
318
+ print "\e[0m"
319
+ else
320
+ Kernel::puts message
321
+ end
322
+ end
323
+ end
324
+
325
+ COS_CLI.start
data/bin/setup CHANGED
@@ -3,6 +3,4 @@ set -euo pipefail
3
3
  IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
6
+ bundle install
@@ -4,21 +4,32 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'cos/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "cos"
8
- spec.version = Cos::VERSION
9
- spec.authors = ["RaymondChou"]
10
- spec.email = ["freezestart@gmail.com"]
7
+ spec.name = 'cos'
8
+ spec.version = COS::VERSION
9
+ spec.authors = ['RaymondChou']
10
+ spec.email = ['freezestart@gmail.com']
11
11
 
12
- spec.summary = %q{Tencent Cloud Object Service Ruby SDK.}
12
+ spec.summary = %q{Tencent COS Ruby SDK.}
13
13
  spec.description = %q{Tencent Cloud Object Service Ruby SDK.}
14
- spec.homepage = "http://github.com/RaymondChou"
14
+ spec.homepage = 'http://github.com/RaymondChou'
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
- spec.bindir = "exe"
18
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
- spec.require_paths = ["lib"]
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|example)/}) }
17
+ spec.test_files = Dir.glob('spec/**/*_spec.rb') + Dir.glob('test/**/*.rb')
18
+ spec.bindir = 'bin'
19
+ spec.executables << 'cos'
20
+ spec.require_paths = ['lib']
21
+ spec.license = 'Apache 2.0'
20
22
 
21
- spec.add_development_dependency "bundler", "~> 1.11"
22
- spec.add_development_dependency "rake", "~> 10.0"
23
- spec.add_development_dependency "minitest", "~> 5.0"
23
+ spec.add_dependency 'rest-client', '~> 1.8'
24
+
25
+ spec.add_runtime_dependency 'thor', '~> 0.19'
26
+
27
+ spec.add_development_dependency 'bundler', '~> 1.10'
28
+ spec.add_development_dependency 'rake', '~> 10.4'
29
+ spec.add_development_dependency 'rspec', '~> 3.3'
30
+ spec.add_development_dependency 'webmock', '~> 1.22'
31
+ spec.add_development_dependency 'simplecov', '~> 0.10'
32
+ spec.add_development_dependency 'minitest', '~> 5.8'
33
+
34
+ spec.required_ruby_version = '>= 1.9.3'
24
35
  end
data/lib/cos.rb CHANGED
@@ -1,5 +1,42 @@
1
- require "cos/version"
1
+ # coding: utf-8
2
2
 
3
- module Cos
4
- # Your code goes here...
5
- end
3
+ require_relative 'cos/version'
4
+ require_relative 'cos/util'
5
+ require_relative 'cos/struct'
6
+ require_relative 'cos/logging'
7
+ require_relative 'cos/exception'
8
+ require_relative 'cos/config'
9
+ require_relative 'cos/signature'
10
+ require_relative 'cos/client'
11
+ require_relative 'cos/bucket'
12
+ require_relative 'cos/http'
13
+ require_relative 'cos/checkpoint'
14
+ require_relative 'cos/slice'
15
+ require_relative 'cos/api'
16
+ require_relative 'cos/resource'
17
+ require_relative 'cos/file'
18
+ require_relative 'cos/dir'
19
+ require_relative 'cos/download'
20
+ require_relative 'cos/tree'
21
+
22
+ module COS
23
+
24
+ # 初始化实例方法
25
+ def self.client(options = {})
26
+ unless @client
27
+
28
+ # Rails配置
29
+ if defined? Rails
30
+ COS::Logging.set_logger(Rails.root.join('log/cos-sdk.log'), Logger::INFO)
31
+ configs = options.merge(config: Rails.root.join('config/cos.yml'))
32
+ @client = COS::Client.new(configs)
33
+ else
34
+ @client = COS::Client.new(options)
35
+ end
36
+
37
+ end
38
+
39
+ @client
40
+ end
41
+
42
+ end