confgit 0.0.3
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/.gitignore +28 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +192 -0
- data/Rakefile +13 -0
- data/VERSION +1 -0
- data/bin/confgit +8 -0
- data/confgit.gemspec +41 -0
- data/etc/bash_completion.d/confgit +39 -0
- data/etc/tools/update_version.rb +14 -0
- data/lib/confgit/cli.rb +143 -0
- data/lib/confgit/locales/en.yml +18 -0
- data/lib/confgit/locales/ja.yml +18 -0
- data/lib/confgit/repo.rb +614 -0
- data/lib/confgit/version.rb +4 -0
- data/lib/confgit/with_color.rb +54 -0
- data/lib/confgit.rb +10 -0
- data/spec/confgit_spec.rb +466 -0
- data/spec/spec_helper.rb +9 -0
- metadata +135 -0
data/lib/confgit/repo.rb
ADDED
@@ -0,0 +1,614 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'pathname'
|
5
|
+
require 'etc'
|
6
|
+
require 'shellwords'
|
7
|
+
require 'open3'
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'json'
|
11
|
+
|
12
|
+
require 'confgit/with_color'
|
13
|
+
|
14
|
+
|
15
|
+
module Confgit
|
16
|
+
|
17
|
+
class Repo
|
18
|
+
ROOT_KEY = 'confgit.root'
|
19
|
+
|
20
|
+
include WithColor
|
21
|
+
|
22
|
+
def initialize(path = '~/.etc/confgit')
|
23
|
+
@base_path = File.expand_path(path)
|
24
|
+
@repos_path = File.join(@base_path, 'repos')
|
25
|
+
|
26
|
+
FileUtils.mkpath(@repos_path)
|
27
|
+
|
28
|
+
@config = read_config(File.join(@base_path, 'confgit.conf'))
|
29
|
+
@repo_path = File.expand_path('current', @repos_path)
|
30
|
+
|
31
|
+
valid_repo unless File.symlink?(@repo_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
# ホスト名
|
35
|
+
def hostname
|
36
|
+
`hostname`.chomp
|
37
|
+
end
|
38
|
+
|
39
|
+
# カレントリポジトリがない場合の処理
|
40
|
+
def valid_repo
|
41
|
+
repo = nil
|
42
|
+
|
43
|
+
repo_each { |file, is_current|
|
44
|
+
repo = file
|
45
|
+
break
|
46
|
+
}
|
47
|
+
|
48
|
+
chrepo(repo || hostname)
|
49
|
+
end
|
50
|
+
|
51
|
+
# リポジトリの変更
|
52
|
+
def chrepo(repo)
|
53
|
+
Dir.chdir(@repos_path) { |path|
|
54
|
+
begin
|
55
|
+
if File.symlink?('current')
|
56
|
+
return if File.readlink('current') == repo
|
57
|
+
File.unlink('current')
|
58
|
+
end
|
59
|
+
|
60
|
+
unless File.exist?(repo)
|
61
|
+
FileUtils.mkpath(repo)
|
62
|
+
|
63
|
+
Dir.chdir(repo) { |path|
|
64
|
+
begin
|
65
|
+
out, err, status = Open3.capture3('git', 'init')
|
66
|
+
$stderr.puts err unless err.empty?
|
67
|
+
rescue => e
|
68
|
+
FileUtils.remove_entry_secure(repo)
|
69
|
+
abort e.to_s
|
70
|
+
end
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
File.symlink(repo, 'current')
|
75
|
+
rescue => e
|
76
|
+
abort e.to_s
|
77
|
+
end
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
# リポジトリの削除
|
82
|
+
def rmrepo(repo, force = false)
|
83
|
+
Dir.chdir(@repos_path) { |path|
|
84
|
+
begin
|
85
|
+
if File.symlink?('current') && File.readlink('current') == repo
|
86
|
+
abort "'#{repo}' is current repository!" unless force
|
87
|
+
File.unlink('current')
|
88
|
+
end
|
89
|
+
|
90
|
+
FileUtils.remove_entry_secure(repo)
|
91
|
+
|
92
|
+
valid_repo unless File.symlink?('current')
|
93
|
+
rescue => e
|
94
|
+
abort e.to_s
|
95
|
+
end
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
# 設定の初期値
|
100
|
+
def default_config
|
101
|
+
{}
|
102
|
+
end
|
103
|
+
|
104
|
+
# 設定の読込み
|
105
|
+
def read_config(file)
|
106
|
+
if File.exist?(file)
|
107
|
+
config = JSON.parse(File.read(file))
|
108
|
+
config = default_config.merge(config)
|
109
|
+
else
|
110
|
+
config = default_config
|
111
|
+
File.write(file, JSON.pretty_generate(config)+"\n")
|
112
|
+
end
|
113
|
+
|
114
|
+
return config
|
115
|
+
end
|
116
|
+
|
117
|
+
# 外部コマンドを定義する
|
118
|
+
def self.define_command(command, *opts)
|
119
|
+
define_method "confgit_#{command}" do |options, *args|
|
120
|
+
args = getargs(args)
|
121
|
+
|
122
|
+
Dir.chdir(@repo_path) { |path|
|
123
|
+
begin
|
124
|
+
args = opts + args
|
125
|
+
args.push(options)
|
126
|
+
system_(command, *args)
|
127
|
+
rescue => e
|
128
|
+
abort e.to_s
|
129
|
+
end
|
130
|
+
}
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# メソッドがない場合
|
135
|
+
def method_missing(name, *args, &block)
|
136
|
+
if name.to_s =~ /^confgit_(.+)$/
|
137
|
+
options = args.shift
|
138
|
+
args = git_args(args).push(options)
|
139
|
+
|
140
|
+
command = $1.gsub(/_/, '-')
|
141
|
+
git(command, *args)
|
142
|
+
|
143
|
+
# abort "#{CMD} '#{$'}' is not a git command. See '#{CMD} --help'.\n"
|
144
|
+
else
|
145
|
+
super
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# 引数を利用可能にする
|
150
|
+
def getargs(args, force = false)
|
151
|
+
args.collect { |x|
|
152
|
+
run = false
|
153
|
+
|
154
|
+
case x
|
155
|
+
when /^-/
|
156
|
+
when /\//
|
157
|
+
run = true
|
158
|
+
else
|
159
|
+
run = force
|
160
|
+
end
|
161
|
+
|
162
|
+
if run
|
163
|
+
repo = File.realpath(@repo_path)
|
164
|
+
path = File.join(repo, x)
|
165
|
+
x = Pathname(path).relative_path_from(Pathname(repo)).to_s
|
166
|
+
end
|
167
|
+
|
168
|
+
x
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
# 引数の最後が Hash ならオプションとして取出す
|
173
|
+
def arg_last_options(args)
|
174
|
+
if args.last && args.last.kind_of?(Hash)
|
175
|
+
args.pop
|
176
|
+
else
|
177
|
+
{}
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# オプションに応じて外部呼出しを行う
|
182
|
+
def system_(command, *args)
|
183
|
+
options = arg_last_options(args)
|
184
|
+
|
185
|
+
if options[:interactive] == false
|
186
|
+
out, err, status = Open3.capture3(command, *args)
|
187
|
+
|
188
|
+
$stdout.print out unless out.empty?
|
189
|
+
$stderr.print err unless err.empty?
|
190
|
+
|
191
|
+
status
|
192
|
+
elsif options[:capture]
|
193
|
+
Open3.capture3(command, *args)
|
194
|
+
else
|
195
|
+
system(command, *args)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# git を呼出す
|
200
|
+
def git(*args)
|
201
|
+
Dir.chdir(@repo_path) { |path|
|
202
|
+
begin
|
203
|
+
system_('git', *args)
|
204
|
+
rescue => e
|
205
|
+
abort e.to_s
|
206
|
+
end
|
207
|
+
}
|
208
|
+
end
|
209
|
+
|
210
|
+
# git コマンドの引数を生成する
|
211
|
+
def git_args(args)
|
212
|
+
args.collect { |item|
|
213
|
+
item = $' if item.kind_of?(String) && %r|^/| =~ item
|
214
|
+
item
|
215
|
+
}
|
216
|
+
end
|
217
|
+
|
218
|
+
# ルートのパスを取得する
|
219
|
+
def root
|
220
|
+
return @root if @root
|
221
|
+
|
222
|
+
# 表示
|
223
|
+
out, err, status = git('config', '--path', '--local', ROOT_KEY, :capture => true)
|
224
|
+
out.chomp!
|
225
|
+
out = '/' if out.empty?
|
226
|
+
|
227
|
+
@root = out
|
228
|
+
end
|
229
|
+
|
230
|
+
# ルートのパスを設定する
|
231
|
+
def root=(value)
|
232
|
+
if value && ! value.empty?
|
233
|
+
git('config', '--path', '--local', ROOT_KEY, value)
|
234
|
+
else
|
235
|
+
git('config', '--unset', '--local', ROOT_KEY)
|
236
|
+
end
|
237
|
+
|
238
|
+
@root = nil
|
239
|
+
end
|
240
|
+
|
241
|
+
# ルートからの相対パス
|
242
|
+
def relative_path(path)
|
243
|
+
root_path = Pathname.new(root)
|
244
|
+
Pathname.new(path).relative_path_from(root_path).to_s
|
245
|
+
end
|
246
|
+
|
247
|
+
# ファイルの hash値を求める
|
248
|
+
def hash_object(file)
|
249
|
+
path = File.expand_path(file)
|
250
|
+
open("| git hash-object \"#{path}\"") {|f|
|
251
|
+
return f.gets.chomp
|
252
|
+
}
|
253
|
+
end
|
254
|
+
|
255
|
+
# 確認プロンプトを表示する
|
256
|
+
def yes?(prompt, y = true)
|
257
|
+
yn = y ? 'Yn' : 'yN'
|
258
|
+
print "#{prompt} [#{yn}]: "
|
259
|
+
|
260
|
+
result = $stdin.gets.chomp
|
261
|
+
|
262
|
+
return y if result.empty?
|
263
|
+
|
264
|
+
if /^(y|yes)$/i =~ result
|
265
|
+
y = true
|
266
|
+
else
|
267
|
+
y = false
|
268
|
+
end
|
269
|
+
|
270
|
+
y
|
271
|
+
end
|
272
|
+
|
273
|
+
# ファイルのコピー(属性は維持する)
|
274
|
+
def filecopy(from, to, exiting = false)
|
275
|
+
begin
|
276
|
+
to_dir = File.dirname(to)
|
277
|
+
FileUtils.mkpath(to_dir)
|
278
|
+
|
279
|
+
if File.exist?(to) && ! File.writable_real?(to)
|
280
|
+
# 書込みできない場合は削除を試みる
|
281
|
+
File.unlink(to)
|
282
|
+
end
|
283
|
+
|
284
|
+
FileUtils.copy(from, to)
|
285
|
+
stat = File.stat(from)
|
286
|
+
File.utime(stat.atime, stat.mtime, to)
|
287
|
+
File.chmod(stat.mode, to)
|
288
|
+
|
289
|
+
return true
|
290
|
+
rescue => e
|
291
|
+
abort e.to_s if exiting
|
292
|
+
$stderr.puts e.to_s
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# ディレクトリ内のファイルを繰返す
|
297
|
+
def dir_each(subdir = '.')
|
298
|
+
Dir.chdir(File.expand_path(subdir, @repo_path)) { |path|
|
299
|
+
Dir.foreach('.') { |file|
|
300
|
+
next if /^(\.git|\.$|\.\.$)/ =~ file
|
301
|
+
|
302
|
+
yield(file)
|
303
|
+
|
304
|
+
if File.directory?(file)
|
305
|
+
Dir.glob("#{file}/**/*", File::FNM_DOTMATCH) { |file|
|
306
|
+
if /(^|\/)(\.git|\.|\.\.)$/ !~ file
|
307
|
+
yield(file)
|
308
|
+
end
|
309
|
+
}
|
310
|
+
end
|
311
|
+
}
|
312
|
+
}
|
313
|
+
end
|
314
|
+
|
315
|
+
# git に管理されているファイルを繰返す
|
316
|
+
def git_each(*args)
|
317
|
+
args = getargs(args, true)
|
318
|
+
files = args.collect { |f| f.shellescape }
|
319
|
+
|
320
|
+
Dir.chdir(@repo_path) { |path|
|
321
|
+
open("| git ls-files --stage --full-name " + files.join(' ')) {|f|
|
322
|
+
while line = f.gets
|
323
|
+
mode, hash, stage, file = line.split
|
324
|
+
|
325
|
+
# file = line.chomp
|
326
|
+
next if /^\.git/ =~ file
|
327
|
+
next if File.directory?(file)
|
328
|
+
|
329
|
+
yield(file, hash)
|
330
|
+
end
|
331
|
+
}
|
332
|
+
}
|
333
|
+
end
|
334
|
+
|
335
|
+
# リポジトリを繰返す
|
336
|
+
def repo_each
|
337
|
+
Dir.chdir(@repos_path) { |path|
|
338
|
+
begin
|
339
|
+
current = File.expand_path(File.readlink('current'))
|
340
|
+
rescue
|
341
|
+
end
|
342
|
+
|
343
|
+
Dir.glob('*') { |file|
|
344
|
+
next if /^current$/ =~ file
|
345
|
+
|
346
|
+
if current && File.realpath(file) == current
|
347
|
+
is_current = true
|
348
|
+
current = nil
|
349
|
+
else
|
350
|
+
is_current = false
|
351
|
+
end
|
352
|
+
|
353
|
+
yield(file, is_current)
|
354
|
+
}
|
355
|
+
|
356
|
+
yield(File.readlink('current'), true) if current
|
357
|
+
}
|
358
|
+
end
|
359
|
+
|
360
|
+
# パスを展開する
|
361
|
+
def expand_path(path, dir = nil)
|
362
|
+
File.expand_path(path, dir).gsub(%r|^/private(/[^/]+)|) { |m|
|
363
|
+
begin
|
364
|
+
subdir = $1
|
365
|
+
m = subdir if File.realpath(subdir) == m
|
366
|
+
rescue
|
367
|
+
end
|
368
|
+
|
369
|
+
m
|
370
|
+
}
|
371
|
+
end
|
372
|
+
|
373
|
+
# オプションを取出す
|
374
|
+
def getopts(args)
|
375
|
+
options = []
|
376
|
+
args.each { |opt|
|
377
|
+
break unless /^-/ =~ opt
|
378
|
+
options << args.shift
|
379
|
+
}
|
380
|
+
|
381
|
+
options
|
382
|
+
end
|
383
|
+
|
384
|
+
# ファイルの更新チェック
|
385
|
+
def modfile?(from, to)
|
386
|
+
! File.exist?(to) || hash_object(from) != hash_object(to)
|
387
|
+
end
|
388
|
+
|
389
|
+
# ファイル属性を文字列にする
|
390
|
+
def mode2str(bits)
|
391
|
+
case bits & 0170000 # S_IFMT
|
392
|
+
when 0010000 # S_IFIFO パイプ
|
393
|
+
mode = 'p'
|
394
|
+
when 0020000 # S_IFCHR キャラクタ・デバイス
|
395
|
+
mode = 'c'
|
396
|
+
when 0040000 # S_IFDIR ディレクトリ
|
397
|
+
mode = 'd'
|
398
|
+
when 0060000 # S_IFBLK ブロック・デバイス
|
399
|
+
mode = 'b'
|
400
|
+
when 0100000 # S_IFREG 通常ファイル
|
401
|
+
mode = '-'
|
402
|
+
when 0120000 # S_IFLNK シンボリックリンク
|
403
|
+
mode = 'l'
|
404
|
+
when 0140000 # S_IFSOCK ソケット
|
405
|
+
mode = 's'
|
406
|
+
when 0160000 # S_IFWHT BSD空白ファイル
|
407
|
+
mode = 'w'
|
408
|
+
end
|
409
|
+
|
410
|
+
mode += 'rwx'*3
|
411
|
+
|
412
|
+
(0..8).each { |i|
|
413
|
+
mask = 1<<i
|
414
|
+
mode[-(i+1)] = '-' if (bits & mask) == 0
|
415
|
+
}
|
416
|
+
|
417
|
+
if (bits & 0001000) != 0 # S_ISVTX スティッキービット
|
418
|
+
if mode[-1] == '-'
|
419
|
+
mode[-1] = 'T'
|
420
|
+
else
|
421
|
+
mode[-1] = 't'
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
mode
|
426
|
+
end
|
427
|
+
|
428
|
+
# コマンド
|
429
|
+
|
430
|
+
# カレントリポジトリの表示・変更
|
431
|
+
def confgit_repo(options, repo = nil)
|
432
|
+
if repo
|
433
|
+
# 変更
|
434
|
+
if options[:remove]
|
435
|
+
rmrepo(repo, options[:force])
|
436
|
+
else
|
437
|
+
chrepo(repo)
|
438
|
+
end
|
439
|
+
else
|
440
|
+
# 表示
|
441
|
+
repo_each { |file, is_current|
|
442
|
+
mark = is_current ? '*' : ' '
|
443
|
+
print "#{mark} #{file}\n"
|
444
|
+
}
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
# ルートの表示・変更
|
449
|
+
def confgit_root(options, value = nil)
|
450
|
+
if options[:remove]
|
451
|
+
# 削除
|
452
|
+
self.root = nil
|
453
|
+
elsif value
|
454
|
+
# 変更
|
455
|
+
self.root = value
|
456
|
+
else
|
457
|
+
# 表示
|
458
|
+
puts root
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
# リポジトリの初期化
|
463
|
+
def confgit_init(options)
|
464
|
+
FileUtils.mkpath(@repo_path)
|
465
|
+
git('init')
|
466
|
+
end
|
467
|
+
|
468
|
+
# ファイルを管理対象に追加
|
469
|
+
def confgit_add(options, *files)
|
470
|
+
confgit_init unless File.exist?(@repo_path)
|
471
|
+
repo = File.realpath(@repo_path)
|
472
|
+
|
473
|
+
files.each { |path|
|
474
|
+
path = expand_path(path)
|
475
|
+
|
476
|
+
if relative_path(path) =~ /^[.]{2}/
|
477
|
+
$stderr.puts "'#{path}' is outside directory"
|
478
|
+
next
|
479
|
+
end
|
480
|
+
|
481
|
+
if File.directory?(path)
|
482
|
+
dir_each(path) { |file|
|
483
|
+
next if File.directory?(file)
|
484
|
+
|
485
|
+
from = File.join(path, file)
|
486
|
+
to = File.join(repo, relative_path(from))
|
487
|
+
|
488
|
+
if filecopy(from, to)
|
489
|
+
git('add', to)
|
490
|
+
end
|
491
|
+
}
|
492
|
+
else
|
493
|
+
from = path
|
494
|
+
to = File.join(repo, relative_path(from))
|
495
|
+
|
496
|
+
if filecopy(from, to)
|
497
|
+
git('add', to)
|
498
|
+
end
|
499
|
+
end
|
500
|
+
}
|
501
|
+
end
|
502
|
+
|
503
|
+
# ファイルを管理対象から削除
|
504
|
+
def confgit_rm(options, *args)
|
505
|
+
return unless File.exist?(@repo_path)
|
506
|
+
|
507
|
+
options = getopts(args)
|
508
|
+
repo = File.realpath(@repo_path)
|
509
|
+
|
510
|
+
files = args.collect { |from|
|
511
|
+
File.join(repo, relative_path(expand_path(from)))
|
512
|
+
}
|
513
|
+
|
514
|
+
git('rm', *(options + files), :interactive => false)
|
515
|
+
end
|
516
|
+
|
517
|
+
# バックアップする
|
518
|
+
def confgit_backup(options, *args)
|
519
|
+
git_each(*args) { |file, hash|
|
520
|
+
next if File.directory?(file)
|
521
|
+
|
522
|
+
from = File.join(root, file)
|
523
|
+
to = File.join(@repo_path, file)
|
524
|
+
|
525
|
+
unless File.exist?(from)
|
526
|
+
with_color(:fg_red) { print "[?] #{file}" }
|
527
|
+
puts
|
528
|
+
next
|
529
|
+
end
|
530
|
+
|
531
|
+
if options[:force] || modfile?(from, to)
|
532
|
+
with_color(:fg_blue) { print "--> #{file}" }
|
533
|
+
write = options[:yes]
|
534
|
+
|
535
|
+
if write == nil
|
536
|
+
# 書込みが決定していない場合
|
537
|
+
write = yes?(nil, false)
|
538
|
+
else
|
539
|
+
puts
|
540
|
+
end
|
541
|
+
|
542
|
+
filecopy(from, to) if write
|
543
|
+
end
|
544
|
+
}
|
545
|
+
|
546
|
+
git('status', :interactive => false)
|
547
|
+
end
|
548
|
+
|
549
|
+
# リストアする
|
550
|
+
def confgit_restore(options, *args)
|
551
|
+
git_each(*args) { |file, hash|
|
552
|
+
next if File.directory?(file)
|
553
|
+
|
554
|
+
from = File.join(@repo_path, file)
|
555
|
+
to = File.join(root, file)
|
556
|
+
|
557
|
+
unless File.exist?(from)
|
558
|
+
with_color(:fg_red) { print "[?] #{file}" }
|
559
|
+
puts
|
560
|
+
next
|
561
|
+
end
|
562
|
+
|
563
|
+
if options[:force] || modfile?(from, to)
|
564
|
+
color = File.writable_real?(to) ? :fg_blue : :fg_magenta
|
565
|
+
with_color(color) { print "<-- #{file}" }
|
566
|
+
write = options[:yes]
|
567
|
+
|
568
|
+
if write == nil
|
569
|
+
# 書込みが決定していない場合
|
570
|
+
write = yes?(nil, false)
|
571
|
+
else
|
572
|
+
puts
|
573
|
+
end
|
574
|
+
|
575
|
+
filecopy(from, to) if write
|
576
|
+
end
|
577
|
+
}
|
578
|
+
end
|
579
|
+
|
580
|
+
# 一覧表示する
|
581
|
+
def confgit_list(options, *args)
|
582
|
+
git_each(*args) { |file, hash|
|
583
|
+
next if File.directory?(file)
|
584
|
+
|
585
|
+
from = File.join(root, file)
|
586
|
+
to = File.join(@repo_path, file)
|
587
|
+
|
588
|
+
if File.exist?(from)
|
589
|
+
stat = File.stat(from)
|
590
|
+
mode = options[:octal] ? stat.mode.to_s(8) : mode2str(stat.mode)
|
591
|
+
user = Etc.getpwuid(stat.uid).name
|
592
|
+
group = Etc.getgrgid(stat.gid).name
|
593
|
+
else
|
594
|
+
mode = ' ' * (options[:octal] ? 6 : 10)
|
595
|
+
user = '-'
|
596
|
+
group = '-'
|
597
|
+
end
|
598
|
+
|
599
|
+
print "#{mode}\t#{user}\t#{group}\t#{from}\n"
|
600
|
+
}
|
601
|
+
end
|
602
|
+
|
603
|
+
# リポジトリのパスを表示
|
604
|
+
def confgit_path(options, subdir = '.')
|
605
|
+
path = File.realpath(File.expand_path(subdir, @repo_path))
|
606
|
+
print path, "\n"
|
607
|
+
end
|
608
|
+
|
609
|
+
# 外部コマンド
|
610
|
+
define_command('tree', '-I', '.git') # tree表示する
|
611
|
+
define_command('tig') # tigで表示する
|
612
|
+
end
|
613
|
+
|
614
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
module Confgit
|
4
|
+
|
5
|
+
module WithColor
|
6
|
+
ESC_CODES = {
|
7
|
+
# Text attributes
|
8
|
+
:clear => 0,
|
9
|
+
:bold => 1,
|
10
|
+
:underscore => 4,
|
11
|
+
:blink => 5,
|
12
|
+
:reverse => 7,
|
13
|
+
:concealed => 8,
|
14
|
+
|
15
|
+
# Foreground colors
|
16
|
+
:fg_black => 30,
|
17
|
+
:fg_red => 31,
|
18
|
+
:fg_green => 32,
|
19
|
+
:fg_yellow => 33,
|
20
|
+
:fg_blue => 34,
|
21
|
+
:fg_magenta => 35,
|
22
|
+
:fg_Cyan => 36,
|
23
|
+
:fg_White => 37,
|
24
|
+
|
25
|
+
# Background colors
|
26
|
+
:bg_black => 40,
|
27
|
+
:bg_red => 41,
|
28
|
+
:bg_green => 42,
|
29
|
+
:bg_yellow => 43,
|
30
|
+
:bg_blue => 44,
|
31
|
+
:bg_magenta => 45,
|
32
|
+
:bg_Cyan => 46,
|
33
|
+
:bg_White => 47,
|
34
|
+
}
|
35
|
+
|
36
|
+
# エスケープシーケンスをセットする
|
37
|
+
def set_color(*colors)
|
38
|
+
colors.each { |color|
|
39
|
+
print "\e[", ESC_CODES[color], "m"
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
# カラー表示する
|
44
|
+
def with_color(*colors)
|
45
|
+
begin
|
46
|
+
set_color(*colors)
|
47
|
+
yield
|
48
|
+
ensure
|
49
|
+
set_color(0)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|