cc 1.1.4 → 1.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.
- checksums.yaml +4 -4
- data/cc.rb +1 -0
- data/enum.rb +141 -0
- data/shell-tools.rb +264 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d516f93659f22c9c1cc96949b6b28a18f9b0a4488df88d0534b93e02d384ec1
|
|
4
|
+
data.tar.gz: cacf6949dfacd5676274f8189eb6f103ab10bbd8449e97d5ed719647c39fbb83
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 439bf5ca21f7ebc3ae75e9f15864e739554aed7d1020a686a1890bf75defcf7e2ce338ae9f47266eda782c8cb8589ae79b9c9581aa0bef7b9bebdebb3305a0d6
|
|
7
|
+
data.tar.gz: 82b49a5413e171490cb09f9ba0057c5efa39642c7c4814b1463fd90b272225d52e547bb23b87188797358f42f14d92a5d953aa11af6acf9e6141f816fde196ea
|
data/cc.rb
CHANGED
data/enum.rb
CHANGED
|
@@ -14,6 +14,51 @@
|
|
|
14
14
|
puts "Enumberable objects could be added head or tail:"
|
|
15
15
|
pp [ 1 , [2], 3 ].unfold_head( :a, :b, :c )
|
|
16
16
|
pp [[1], 2 , [3]].unfold_tail(*[:x, :y, :z])
|
|
17
|
+
|
|
18
|
+
puts "Hash could project and serialize on a subscope as a sub hash or array:"
|
|
19
|
+
{a: 1 ,b: 2,c: 3}.project(:a, :c) # => {:a=>1, :c=>3}
|
|
20
|
+
{a: 1 ,b: 2,c: 3}.serialize(:a, :c) # => [1, 3]
|
|
21
|
+
|
|
22
|
+
puts "Array could mapping with a subscope as a key-val array:"
|
|
23
|
+
a = [[1,2,3], [4,5,6], [7,8]]
|
|
24
|
+
a.mapping [:a, :b, :c] # => [{:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}, {:a=>7, :b=>8, :c=>nil}]
|
|
25
|
+
a.mapping [:a, :d] # => [{:a=>1, :d=>2}, {:a=>4, :d=>5}, {:a=>7, :d=>8}]
|
|
26
|
+
|
|
27
|
+
puts "Building a RangeTree to query a range of records:"
|
|
28
|
+
pp(rt = RangeTree.new(records = [
|
|
29
|
+
[[1, 3], :a],
|
|
30
|
+
[[2, 4], :b],
|
|
31
|
+
[[2, 7], :c],
|
|
32
|
+
[[6], :d],
|
|
33
|
+
[[4, 5], :e],
|
|
34
|
+
[[8], :f],
|
|
35
|
+
[[10], :g],
|
|
36
|
+
:y,
|
|
37
|
+
:z
|
|
38
|
+
]))
|
|
39
|
+
# {1=>{3=>[:a]},
|
|
40
|
+
# 2=>{4=>[:b], 7=>[:c]},
|
|
41
|
+
# 6=>{6=>[:d]},
|
|
42
|
+
# 4=>{5=>[:e]},
|
|
43
|
+
# 8=>{8=>[:f]},
|
|
44
|
+
# 10=>{10=>[:g]},
|
|
45
|
+
# 0=>{0=>[:y, :z]}}
|
|
46
|
+
|
|
47
|
+
pp rt.query( Range.new(1,2) )
|
|
48
|
+
# ["complete", {[1, 3]=>[:a]}]
|
|
49
|
+
|
|
50
|
+
pp rt.query( [4,7] )
|
|
51
|
+
# ["complete", {[2, 7]=>[:c]}]
|
|
52
|
+
|
|
53
|
+
pp rt.query( 6 )
|
|
54
|
+
# ["complete", {[2, 7]=>[:c], [6, 6]=>[:d]}]
|
|
55
|
+
|
|
56
|
+
pp rt.query( [1,6] )
|
|
57
|
+
# ["partial",
|
|
58
|
+
# {[1, 3]=>[:a], [2, 4]=>[:b], [2, 7]=>[:c], [6, 6]=>[:d], [4, 5]=>[:e]}]
|
|
59
|
+
|
|
60
|
+
pp rt.query( :a )
|
|
61
|
+
# ["unformal", {[0, 0]=>[]}]
|
|
17
62
|
=end
|
|
18
63
|
|
|
19
64
|
module Enumerable
|
|
@@ -75,3 +120,99 @@ class Array
|
|
|
75
120
|
return new_records
|
|
76
121
|
end
|
|
77
122
|
end
|
|
123
|
+
|
|
124
|
+
class RangeTree < Hash
|
|
125
|
+
attr_reader :a
|
|
126
|
+
|
|
127
|
+
# Records := [ [Range|Point, Record] | Record, ... ]
|
|
128
|
+
def initialize records
|
|
129
|
+
merge records
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def merge records
|
|
133
|
+
records.each do|record|
|
|
134
|
+
head, rest = record
|
|
135
|
+
if head.is_a?(Range)
|
|
136
|
+
starter, finisher = head.min, head.max
|
|
137
|
+
elsif head.is_a?(Array)
|
|
138
|
+
starter, finisher = head.sort.first, head.sort.last
|
|
139
|
+
elsif head.is_a?(Numeric)
|
|
140
|
+
starter, finisher = head, head
|
|
141
|
+
else
|
|
142
|
+
starter, finisher, rest = 0, 0, record
|
|
143
|
+
end
|
|
144
|
+
self[starter] ||= {}
|
|
145
|
+
self[starter][finisher] ||= []
|
|
146
|
+
self[starter][finisher] << rest
|
|
147
|
+
self[starter][finisher].uniq!
|
|
148
|
+
end
|
|
149
|
+
@start_keys = self.keys.sort
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def update_key
|
|
153
|
+
@start_keys = self.keys.sort
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def unikey target
|
|
157
|
+
if target.is_a?(Range)
|
|
158
|
+
skey, fkey = target.min, target.max
|
|
159
|
+
elsif target.is_a?(Array)
|
|
160
|
+
skey, fkey = target.sort.first, target.sort.last
|
|
161
|
+
elsif target.is_a?(Numeric)
|
|
162
|
+
skey, fkey = target, target
|
|
163
|
+
else
|
|
164
|
+
skey, fkey = 0, 0
|
|
165
|
+
end
|
|
166
|
+
return skey, fkey
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def locate target
|
|
170
|
+
skey, fkey = unikey(target)
|
|
171
|
+
keys = []
|
|
172
|
+
start_keys = self.keys.sort
|
|
173
|
+
candi_keys = start_keys.select{|sk|sk<=skey}
|
|
174
|
+
candi_keys.each do|candi_key|
|
|
175
|
+
finish_keys = self[candi_key].keys.sort
|
|
176
|
+
cand_keys = finish_keys.select{|fk|fk>=fkey}.map{|cand_key|[candi_key, cand_key]}
|
|
177
|
+
keys += cand_keys
|
|
178
|
+
end
|
|
179
|
+
return keys
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def search target
|
|
183
|
+
keys = []
|
|
184
|
+
skey, fkey = unikey(target)
|
|
185
|
+
self.each do|sk, candis|
|
|
186
|
+
candis.each do|fk, cands|
|
|
187
|
+
next if fkey < sk || fk < skey
|
|
188
|
+
keys << [sk,fk]
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
return keys
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def index target
|
|
195
|
+
keys = locate(target)
|
|
196
|
+
if keys.empty?
|
|
197
|
+
return 'partial', search(target)
|
|
198
|
+
elsif keys==[[0,0]]
|
|
199
|
+
return 'unformal', [[0,0]]
|
|
200
|
+
else
|
|
201
|
+
return 'complete', keys
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def query target
|
|
206
|
+
result = {}
|
|
207
|
+
state, keys = index(target)
|
|
208
|
+
keys.each do|keyp|
|
|
209
|
+
sk, fk = keyp
|
|
210
|
+
result[keyp] = self[sk][fk]
|
|
211
|
+
end
|
|
212
|
+
if state=='unformal'
|
|
213
|
+
self[0] ||= {}
|
|
214
|
+
result[[0,0]] = (self[0][0]||[]).include?(target) ? target : []
|
|
215
|
+
end
|
|
216
|
+
return state, result
|
|
217
|
+
end
|
|
218
|
+
end
|
data/shell-tools.rb
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: utf-8
|
|
3
|
+
|
|
4
|
+
require 'io/console'
|
|
5
|
+
|
|
6
|
+
class ProgressBar
|
|
7
|
+
# 预设主题
|
|
8
|
+
THEMES = {
|
|
9
|
+
classic: { fill: '█', empty: '░', left: '[', right: ']', arrow: '>' },
|
|
10
|
+
modern: { fill: '●', empty: '○', left: '', right: '', arrow: '▶' },
|
|
11
|
+
blocks: { fill: '■', empty: '□', left: '│', right: '│', arrow: '▶' },
|
|
12
|
+
dots: { fill: '◉', empty: '◎', left: '⟨', right: '⟩', arrow: '→' },
|
|
13
|
+
minimal: { fill: '=', empty: '-', left: '[', right: ']', arrow: '>' },
|
|
14
|
+
hearts: { fill: '♥', empty: '♡', left: ' ', right: ' ', arrow: '💕' }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
attr_reader :total, :current, :start_time
|
|
18
|
+
|
|
19
|
+
def initialize(total, title: "Processing", theme: :modern, width: 40, show_percentage: true, show_eta: true, color: true)
|
|
20
|
+
@total = total
|
|
21
|
+
@current = 0
|
|
22
|
+
@title = title
|
|
23
|
+
@theme = THEMES[theme] || THEMES[:modern]
|
|
24
|
+
@width = width
|
|
25
|
+
@show_percentage = show_percentage
|
|
26
|
+
@show_eta = show_eta
|
|
27
|
+
@color = color
|
|
28
|
+
@start_time = Time.now
|
|
29
|
+
@last_update = 0
|
|
30
|
+
@mutex = Mutex.new
|
|
31
|
+
|
|
32
|
+
# ANSI 颜色代码
|
|
33
|
+
@colors = {
|
|
34
|
+
reset: "\e[0m",
|
|
35
|
+
bold: "\e[1m",
|
|
36
|
+
green: "\e[32m",
|
|
37
|
+
yellow: "\e[33m",
|
|
38
|
+
blue: "\e[34m",
|
|
39
|
+
magenta: "\e[35m",
|
|
40
|
+
cyan: "\e[36m",
|
|
41
|
+
gray: "\e[90m"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# 隐藏光标
|
|
45
|
+
print "\e[?25l"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# 更新进度
|
|
49
|
+
def update(step = 1)
|
|
50
|
+
@mutex.synchronize do
|
|
51
|
+
@current += step
|
|
52
|
+
@current = @total if @current > @total
|
|
53
|
+
refresh if should_refresh?
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# 直接设置进度
|
|
58
|
+
def set(value)
|
|
59
|
+
@mutex.synchronize do
|
|
60
|
+
@current = [[value, @total].min, 0].max
|
|
61
|
+
refresh
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# 完成进度条
|
|
66
|
+
def finish(message: "Done!")
|
|
67
|
+
@mutex.synchronize do
|
|
68
|
+
@current = @total
|
|
69
|
+
refresh
|
|
70
|
+
puts # 换行
|
|
71
|
+
show_cursor
|
|
72
|
+
puts colorize(message, :green) unless message.empty?
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# 显示统计信息
|
|
77
|
+
def stats
|
|
78
|
+
elapsed = Time.now - @start_time
|
|
79
|
+
rate = @current / elapsed rescue 0
|
|
80
|
+
{
|
|
81
|
+
total: @total,
|
|
82
|
+
current: @current,
|
|
83
|
+
percentage: (@current.to_f / @total * 100).round(1),
|
|
84
|
+
elapsed: format_time(elapsed),
|
|
85
|
+
eta: format_time(eta),
|
|
86
|
+
rate: rate.round(2)
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# 清理资源
|
|
91
|
+
def dispose
|
|
92
|
+
show_cursor
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def should_refresh?
|
|
98
|
+
now = Time.now.to_f
|
|
99
|
+
return false if now - @last_update < 0.05 # 限制刷新率 20fps
|
|
100
|
+
@last_update = now
|
|
101
|
+
true
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def refresh
|
|
105
|
+
percentage = @current.to_f / @total
|
|
106
|
+
filled = (percentage * @width).round
|
|
107
|
+
empty = @width - filled
|
|
108
|
+
|
|
109
|
+
bar = @theme[:fill] * filled +
|
|
110
|
+
(@current < @total ? @theme[:arrow] : @theme[:fill]) +
|
|
111
|
+
@theme[:empty] * [empty - 1, 0].max
|
|
112
|
+
|
|
113
|
+
# 构建输出字符串
|
|
114
|
+
parts = []
|
|
115
|
+
|
|
116
|
+
# 标题
|
|
117
|
+
parts << colorize(sprintf("%-15s", @title), :bold)
|
|
118
|
+
|
|
119
|
+
# 进度条
|
|
120
|
+
bar_color = percentage >= 1.0 ? :green : (percentage > 0.5 ? :cyan : :yellow)
|
|
121
|
+
parts << "#{@theme[:left]}#{colorize(bar, bar_color)}#{@theme[:right]}"
|
|
122
|
+
|
|
123
|
+
# 百分比
|
|
124
|
+
if @show_percentage
|
|
125
|
+
parts << colorize(sprintf("%6.1f%%", percentage * 100), :magenta)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# 计数
|
|
129
|
+
parts << colorize("[#{@current}/#{@total}]", :gray)
|
|
130
|
+
|
|
131
|
+
# ETA
|
|
132
|
+
if @show_eta && @current > 0 && @current < @total
|
|
133
|
+
parts << colorize("ETA: #{format_time(eta)}", :blue)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# 清除行并输出
|
|
137
|
+
print "\r\e[K" + parts.join(' ')
|
|
138
|
+
$stdout.flush
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def eta
|
|
142
|
+
return 0 if @current == 0
|
|
143
|
+
elapsed = Time.now - @start_time
|
|
144
|
+
remaining = @total - @current
|
|
145
|
+
(elapsed / @current) * remaining
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def format_time(seconds)
|
|
149
|
+
return "0s" if seconds < 1
|
|
150
|
+
if seconds < 60
|
|
151
|
+
"#{seconds.round}s"
|
|
152
|
+
elsif seconds < 3600
|
|
153
|
+
"#{(seconds / 60).floor}m #{(seconds % 60).round}s"
|
|
154
|
+
else
|
|
155
|
+
"#{(seconds / 3600).floor}h #{((seconds % 3600) / 60).floor}m"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def colorize(text, color)
|
|
160
|
+
return text unless @color
|
|
161
|
+
"#{@colors[color]}#{text}#{@colors[:reset]}"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def show_cursor
|
|
165
|
+
print "\e[?25h"
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# 辅助方法(用于示例中的颜色输出)
|
|
170
|
+
def colorize(text, color)
|
|
171
|
+
colors = { green: "\e[32m", yellow: "\e[33m", gray: "\e[90m", reset: "\e[0m" }
|
|
172
|
+
"#{colors[color]}#{text}#{colors[:reset]}"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
=begin
|
|
176
|
+
# ==================== 使用示例 ====================
|
|
177
|
+
puts "🚀 Ruby Fancy Progress Bar Demo"
|
|
178
|
+
puts "=" * 50
|
|
179
|
+
|
|
180
|
+
# 示例 1: 基础用法
|
|
181
|
+
puts "\n📊 示例 1: 基础文件处理"
|
|
182
|
+
bar = ProgressBar.new(100, title: "Downloading", theme: :modern)
|
|
183
|
+
100.times do |i|
|
|
184
|
+
sleep(0.03) # 模拟工作
|
|
185
|
+
bar.update
|
|
186
|
+
end
|
|
187
|
+
bar.finish(message: "✅ 下载完成!")
|
|
188
|
+
|
|
189
|
+
# 示例 2: 不同主题展示
|
|
190
|
+
puts "\n🎨 示例 2: 主题展示"
|
|
191
|
+
[:classic, :blocks, :dots, :minimal].each do |theme_name|
|
|
192
|
+
bar = ProgressBar.new(50, title: theme_name.to_s, theme: theme_name, width: 30)
|
|
193
|
+
50.times do
|
|
194
|
+
sleep(0.02)
|
|
195
|
+
bar.update
|
|
196
|
+
end
|
|
197
|
+
bar.finish(message: "")
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# 示例 3: 批量任务处理(带错误处理)
|
|
201
|
+
puts "\n📁 示例 3: 批量文件处理"
|
|
202
|
+
files = (1..20).map { |n| "file_#{n}.txt" }
|
|
203
|
+
bar = ProgressBar.new(files.size, title: "Processing", theme: :hearts, color: true)
|
|
204
|
+
processed = []
|
|
205
|
+
files.each do |file|
|
|
206
|
+
sleep(0.1) # 模拟处理时间
|
|
207
|
+
processed << file
|
|
208
|
+
bar.update
|
|
209
|
+
# 模拟偶尔的错误
|
|
210
|
+
if rand < 0.1
|
|
211
|
+
print "\n⚠️ #{colorize("警告: #{file} 处理异常", :yellow)}"
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
bar.finish(message: "✨ 处理了 #{processed.size} 个文件")
|
|
215
|
+
|
|
216
|
+
# 示例 4: 嵌套进度条(多阶段任务)
|
|
217
|
+
puts "\n🏗️ 示例 4: 多阶段构建任务"
|
|
218
|
+
phases = [
|
|
219
|
+
{ name: "Compiling", items: 30 },
|
|
220
|
+
{ name: "Testing", items: 20 },
|
|
221
|
+
{ name: "Packaging", items: 15 }
|
|
222
|
+
]
|
|
223
|
+
phases.each do |phase|
|
|
224
|
+
puts "\n阶段: #{phase[:name]}"
|
|
225
|
+
bar = ProgressBar.new(phase[:items], title: phase[:name], theme: :modern)
|
|
226
|
+
phase[:items].times do
|
|
227
|
+
sleep(0.05)
|
|
228
|
+
bar.update
|
|
229
|
+
end
|
|
230
|
+
bar.finish(message: "")
|
|
231
|
+
end
|
|
232
|
+
puts colorize("🎉 所有阶段完成!", :green)
|
|
233
|
+
|
|
234
|
+
# 示例 5: 实时速度显示(大数据处理)
|
|
235
|
+
puts "\n⚡ 示例 5: 大数据处理(带实时统计)"
|
|
236
|
+
total_items = 1000
|
|
237
|
+
bar = ProgressBar.new(total_items, title: "BigData", theme: :blocks, width: 50)
|
|
238
|
+
total_items.times do |i|
|
|
239
|
+
sleep(0.01) # 模拟快速处理
|
|
240
|
+
# 每 100 个显示一次统计
|
|
241
|
+
if i % 100 == 0 && i > 0
|
|
242
|
+
s = bar.stats
|
|
243
|
+
print "\n #{colorize("速度: #{s[:rate]} items/s | 已用: #{s[:elapsed]} | 剩余: #{s[:eta]}", :gray)}"
|
|
244
|
+
end
|
|
245
|
+
bar.update
|
|
246
|
+
end
|
|
247
|
+
bar.finish
|
|
248
|
+
|
|
249
|
+
# 示例 6: 手动控制进度(非均匀任务)
|
|
250
|
+
puts "\n🎮 示例 6: 手动进度控制(非均匀任务)"
|
|
251
|
+
bar = ProgressBar.new(100, title: "Uploading", theme: :dots)
|
|
252
|
+
# 模拟不同大小的文件上传
|
|
253
|
+
uploads = [10, 25, 5, 30, 20, 10]
|
|
254
|
+
uploads.each_with_index do |size, idx|
|
|
255
|
+
sleep(0.3) # 模拟上传时间
|
|
256
|
+
bar.set(bar.current + size)
|
|
257
|
+
print "\n 上传了 chunk_#{idx + 1}.bin (#{size}MB)"
|
|
258
|
+
end
|
|
259
|
+
bar.finish(message: "上传完成")
|
|
260
|
+
# 确保光标显示
|
|
261
|
+
print "\e[?25h"
|
|
262
|
+
puts "\n" + "=" * 50
|
|
263
|
+
puts "✨ 所有演示完成! 你可以复制 ProgressBar 类到你的项目中使用。"
|
|
264
|
+
=end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matt
|
|
@@ -32,6 +32,7 @@ files:
|
|
|
32
32
|
- monkey-patch.rb
|
|
33
33
|
- number.rb
|
|
34
34
|
- regexp.rb
|
|
35
|
+
- shell-tools.rb
|
|
35
36
|
- string.rb
|
|
36
37
|
- tree.rb
|
|
37
38
|
homepage: https://github.com/ChenMeng1365/custom-core
|