bitferry 0.0.5 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +10 -0
- data/bin/bitferryfx +3 -0
- data/lib/bitferry/cli.rb +13 -12
- data/lib/bitferry/fx.rb +131 -0
- data/lib/bitferry.rb +77 -14
- metadata +50 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f67881c016dae96456c1d0b1baee9b3949eb9bfd46aa5f96cf7232d219c1d7d8
|
4
|
+
data.tar.gz: a5613291cc0b050ce243c8cf4142d8020c363100b0655cf6fcb405c0b380daba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 670ea9837098777a1dfa56df30283f2d6fac41df303003a33cd1e80711d5c4967f09ab72ba2247dbdb00c57830f0310825a064397f5db463e0ee927de72ce6dc
|
7
|
+
data.tar.gz: 433fb3934ae559cf42eb0a20d051923c0dc5e93eed32f5249dc95a1dde1930d3982fb925efd64f03d418fa9fefa54a74959ec87118c3c26d05ba0927667ee0cf
|
data/CHANGES.md
CHANGED
data/bin/bitferryfx
ADDED
data/lib/bitferry/cli.rb
CHANGED
@@ -110,6 +110,7 @@ def obtain_password
|
|
110
110
|
end
|
111
111
|
|
112
112
|
|
113
|
+
Bitferry.ui = :cli
|
113
114
|
Bitferry.log.level = Logger::DEBUG if $DEBUG
|
114
115
|
|
115
116
|
|
@@ -120,7 +121,7 @@ Clamp do
|
|
120
121
|
|
121
122
|
|
122
123
|
option '--version', :flag, 'Print version' do
|
123
|
-
puts Bitferry::VERSION
|
124
|
+
$stdout.puts Bitferry::VERSION
|
124
125
|
exit
|
125
126
|
end
|
126
127
|
|
@@ -144,26 +145,26 @@ Clamp do
|
|
144
145
|
def execute
|
145
146
|
Bitferry.restore
|
146
147
|
unless (xs = Bitferry::Volume.intact).empty?
|
147
|
-
puts '# Intact volumes'
|
148
|
-
puts
|
148
|
+
$stdout.puts '# Intact volumes'
|
149
|
+
$stdout.puts
|
149
150
|
xs.each do |volume|
|
150
|
-
puts " #{volume.tag} #{volume.root}"
|
151
|
+
$stdout.puts " #{volume.tag} #{volume.root}"
|
151
152
|
end
|
152
153
|
end
|
153
154
|
unless (xs = Bitferry::Task.intact).empty?
|
154
|
-
puts
|
155
|
-
puts '# Intact tasks'
|
156
|
-
puts
|
155
|
+
$stdout.puts
|
156
|
+
$stdout.puts '# Intact tasks'
|
157
|
+
$stdout.puts
|
157
158
|
xs.each do |task|
|
158
|
-
puts " #{task.tag} #{task.show_status}"
|
159
|
+
$stdout.puts " #{task.tag} #{task.show_status}"
|
159
160
|
end
|
160
161
|
end
|
161
162
|
if !(xs = Bitferry::Task.stale).empty? && Bitferry.verbosity == :verbose
|
162
|
-
puts
|
163
|
-
puts '# Stale tasks'
|
164
|
-
puts
|
163
|
+
$stdout.puts
|
164
|
+
$stdout.puts '# Stale tasks'
|
165
|
+
$stdout.puts
|
165
166
|
xs.each do |task|
|
166
|
-
puts " #{task.tag} #{task.show_status}"
|
167
|
+
$stdout.puts " #{task.tag} #{task.show_status}"
|
167
168
|
end
|
168
169
|
end
|
169
170
|
end
|
data/lib/bitferry/fx.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'fox16'
|
2
|
+
require 'stringio'
|
3
|
+
require 'bitferry'
|
4
|
+
|
5
|
+
|
6
|
+
include Fox
|
7
|
+
|
8
|
+
|
9
|
+
class Output < StringIO
|
10
|
+
|
11
|
+
def initialize(app, output)
|
12
|
+
super('rw+')
|
13
|
+
@app = app
|
14
|
+
@output = output
|
15
|
+
@output.text = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def write(*args) = @app.runOnUiThread { @output.appendText(args.join) }
|
19
|
+
|
20
|
+
def flush = nil
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
class UI < FXMainWindow
|
26
|
+
|
27
|
+
def initialize(app)
|
28
|
+
super(@app = app, 'BitferryFX', width: 400, height: 300)
|
29
|
+
top_frame = FXVerticalFrame.new(self, opts: LAYOUT_FILL)
|
30
|
+
tabs = FXTabBook.new(top_frame, opts: LAYOUT_FILL)
|
31
|
+
output_tab = FXTabItem.new(tabs, 'Output')
|
32
|
+
@output = FXText.new(tabs)
|
33
|
+
tasks_tab = FXTabItem.new(tabs, 'Tasks')
|
34
|
+
@tasks = FXTable.new(tabs)
|
35
|
+
@tasks.tableStyle |= TABLE_COL_SIZABLE | TABLE_NO_COLSELECT | TABLE_READONLY
|
36
|
+
@tasks.rowHeaderMode = LAYOUT_FIX_WIDTH
|
37
|
+
@tasks.rowHeaderWidth = 0
|
38
|
+
volumes_tab = FXTabItem.new(tabs, 'Volumes')
|
39
|
+
@volumes = FXTable.new(tabs)
|
40
|
+
@volumes.tableStyle |= TABLE_COL_SIZABLE | TABLE_NO_COLSELECT | TABLE_READONLY
|
41
|
+
@volumes.rowHeaderMode = LAYOUT_FIX_WIDTH
|
42
|
+
@volumes.rowHeaderWidth = 0
|
43
|
+
@progress = FXProgressBar.new(top_frame, height: 16, opts: LAYOUT_FILL_X | LAYOUT_FIX_HEIGHT)
|
44
|
+
controls = FXPacker.new(top_frame, opts: LAYOUT_FILL_X)
|
45
|
+
@simulate = FXCheckButton.new(controls, "&Simulation mode (dry run)\tPrevent operations from making any on-disk changes")
|
46
|
+
@simulate.checkState = Bitferry.simulate?
|
47
|
+
@verbose = FXCheckButton.new(controls, "&Verbose mode\tOutput internal logging information")
|
48
|
+
@verbose.checkState = false
|
49
|
+
buttons = FXPacker.new(top_frame, opts: LAYOUT_FILL_X | PACK_UNIFORM_WIDTH | FRAME_SUNKEN)
|
50
|
+
@process = FXButton.new(buttons, "&Process\tProcess all intact tasks", opts: BUTTON_NORMAL | BUTTON_INITIAL | BUTTON_DEFAULT | LAYOUT_SIDE_LEFT)
|
51
|
+
@process.connect(SEL_COMMAND) { process }
|
52
|
+
@process.setFocus
|
53
|
+
@quit = FXButton.new(buttons, "&Quit\tStop any pending operations and exit", opts: BUTTON_NORMAL | LAYOUT_SIDE_RIGHT)
|
54
|
+
@quit.connect(SEL_COMMAND) { exit }
|
55
|
+
@reload = FXButton.new(buttons, "&Reload\tReread volumes and tasks to capture volume changes", opts: BUTTON_NORMAL | LAYOUT_SIDE_RIGHT)
|
56
|
+
@reload.connect(SEL_COMMAND) { reset }
|
57
|
+
@sensible = [@process, @reload] # Controls which must be disabled during processing
|
58
|
+
reset
|
59
|
+
end
|
60
|
+
|
61
|
+
def process
|
62
|
+
Bitferry.simulate = @simulate.checked?
|
63
|
+
Bitferry.verbosity = @verbose.checked? ? :verbose : :default
|
64
|
+
@progress.setBarColor(:blue)
|
65
|
+
@progress.progress = 0
|
66
|
+
@output.text = nil
|
67
|
+
Thread.new {
|
68
|
+
@app.runOnUiThread { @sensible.each(&:disable) }
|
69
|
+
begin
|
70
|
+
Bitferry.process { |total, processed, failed|
|
71
|
+
@app.runOnUiThread {
|
72
|
+
@progress.setBarColor(:red) if failed > 0
|
73
|
+
@progress.progress = processed
|
74
|
+
@progress.total = total
|
75
|
+
}
|
76
|
+
}
|
77
|
+
ensure
|
78
|
+
@app.runOnUiThread { @sensible.each(&:enable) }
|
79
|
+
end
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def reset
|
84
|
+
Bitferry.restore
|
85
|
+
@progress.progress = 0
|
86
|
+
$stdout = Output.new(@app, @output)
|
87
|
+
Bitferry::Logging.log = log = Logger.new($stdout)
|
88
|
+
log.progname = :bitferryfx
|
89
|
+
log.level = Logger::WARN
|
90
|
+
#
|
91
|
+
@volumes.setTableSize(Bitferry::Volume.intact.size, 2)
|
92
|
+
@volumes.setColumnText(0, 'Volume')
|
93
|
+
@volumes.setColumnText(1, 'Root')
|
94
|
+
i = 0
|
95
|
+
Bitferry::Volume.intact.each do |v|
|
96
|
+
@volumes.setItemText(i, 0, v.tag)
|
97
|
+
@volumes.setItemText(i, 1, v.root.to_s)
|
98
|
+
i += 1
|
99
|
+
end
|
100
|
+
#
|
101
|
+
@tasks.setTableSize(Bitferry::Task.intact.size, 4)
|
102
|
+
@tasks.setColumnText(0, 'Task')
|
103
|
+
@tasks.setColumnText(1, 'Operation')
|
104
|
+
@tasks.setColumnText(2, 'Source')
|
105
|
+
@tasks.setColumnText(3, 'Destination')
|
106
|
+
i = 0
|
107
|
+
Bitferry::Task.intact.each do |t|
|
108
|
+
@tasks.setItemText(i, 0, t.tag)
|
109
|
+
@tasks.setItemText(i, 1, t.show_operation)
|
110
|
+
@tasks.setItemText(i, 2, t.source.show_status)
|
111
|
+
@tasks.setItemText(i, 3, t.destination.show_status)
|
112
|
+
i += 1
|
113
|
+
end
|
114
|
+
#
|
115
|
+
end
|
116
|
+
|
117
|
+
def create
|
118
|
+
super
|
119
|
+
show(PLACEMENT_SCREEN)
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
FXApp.new do |app|
|
126
|
+
Bitferry.verbosity = :verbose
|
127
|
+
Bitferry.ui = :gui
|
128
|
+
UI.new(app)
|
129
|
+
app.create
|
130
|
+
app.run
|
131
|
+
end
|
data/lib/bitferry.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'date'
|
3
3
|
require 'open3'
|
4
|
+
require 'base64'
|
4
5
|
require 'logger'
|
6
|
+
require 'openssl'
|
5
7
|
require 'pathname'
|
6
|
-
require 'neatjson'
|
7
8
|
require 'rbconfig'
|
8
9
|
require 'fileutils'
|
9
10
|
require 'shellwords'
|
@@ -12,7 +13,7 @@ require 'shellwords'
|
|
12
13
|
module Bitferry
|
13
14
|
|
14
15
|
|
15
|
-
VERSION = '0.0.
|
16
|
+
VERSION = '0.0.7'
|
16
17
|
|
17
18
|
|
18
19
|
module Logging
|
@@ -24,6 +25,7 @@ module Bitferry
|
|
24
25
|
end
|
25
26
|
@log
|
26
27
|
end
|
28
|
+
def self.log=(log) @log = log end
|
27
29
|
def log = Logging.log
|
28
30
|
end
|
29
31
|
|
@@ -85,9 +87,11 @@ module Bitferry
|
|
85
87
|
end
|
86
88
|
|
87
89
|
|
88
|
-
def self.
|
90
|
+
def self.intact_tasks = Volume.intact.collect { |volume| volume.intact_tasks }.flatten.uniq
|
91
|
+
|
92
|
+
def self.process(*tags, &block)
|
89
93
|
log.info('processing tasks')
|
90
|
-
tasks =
|
94
|
+
tasks = intact_tasks
|
91
95
|
if tags.empty?
|
92
96
|
process = tasks
|
93
97
|
else
|
@@ -102,7 +106,17 @@ module Bitferry
|
|
102
106
|
end
|
103
107
|
end
|
104
108
|
end
|
105
|
-
|
109
|
+
tasks = process.uniq
|
110
|
+
total = tasks.size
|
111
|
+
processed = 0
|
112
|
+
failed = 0
|
113
|
+
result = tasks.all? do |task|
|
114
|
+
r = task.process
|
115
|
+
processed += 1
|
116
|
+
failed += 1 unless r
|
117
|
+
yield(total, processed, failed) if block_given?
|
118
|
+
r
|
119
|
+
end
|
106
120
|
result ? log.info('tasks processed') : log.warn('task process failure(s) reported')
|
107
121
|
result
|
108
122
|
end
|
@@ -137,6 +151,11 @@ module Bitferry
|
|
137
151
|
def self.verbosity=(mode) @verbosity = mode end
|
138
152
|
|
139
153
|
|
154
|
+
@ui = :cli
|
155
|
+
def self.ui = @ui
|
156
|
+
def self.ui=(ui) @ui = ui end
|
157
|
+
|
158
|
+
|
140
159
|
# Return true if run in the real Windows environment (e.g. not in real *NIX or various emulation layers such as MSYS, Cygwin etc.)
|
141
160
|
def self.windows?
|
142
161
|
@windows ||= /^(mingw)/.match?(RbConfig::CONFIG['target_os']) # RubyInstaller's MRI, other MinGW-build MRI
|
@@ -388,6 +407,7 @@ module Bitferry
|
|
388
407
|
|
389
408
|
|
390
409
|
def store
|
410
|
+
require 'neatjson'
|
391
411
|
tasks.each(&:commit)
|
392
412
|
hash = JSON.neat_generate(externalize, short: false, wrap: 200, afterColon: 1, afterComma: 1)
|
393
413
|
if Bitferry.simulate?
|
@@ -657,9 +677,9 @@ module Bitferry
|
|
657
677
|
def self.exec(*args)
|
658
678
|
cmd = [executable] + args
|
659
679
|
log.debug(cmd.collect(&:shellescape).join(' '))
|
660
|
-
stdout, status = Open3.
|
680
|
+
stdout, status = Open3.capture2e(*cmd)
|
661
681
|
unless status.success?
|
662
|
-
msg = "rclone exit code #{status.
|
682
|
+
msg = "rclone exit code #{status.exitstatus}"
|
663
683
|
log.error(msg)
|
664
684
|
raise RuntimeError, msg
|
665
685
|
end
|
@@ -667,10 +687,26 @@ module Bitferry
|
|
667
687
|
end
|
668
688
|
|
669
689
|
|
670
|
-
|
690
|
+
# https://github.com/rclone/rclone/blob/master/fs/config/obscure/obscure.go
|
691
|
+
SECRET = "\x9c\x93\x5b\x48\x73\x0a\x55\x4d\x6b\xfd\x7c\x63\xc8\x86\xa9\x2b\xd3\x90\x19\x8e\xb8\x12\x8a\xfb\xf4\xde\x16\x2b\x8b\x95\xf6\x38"
|
671
692
|
|
672
693
|
|
673
|
-
def self.
|
694
|
+
def self.obscure(plain)
|
695
|
+
cipher = OpenSSL::Cipher.new('AES-256-CTR')
|
696
|
+
cipher.encrypt
|
697
|
+
cipher.key = SECRET
|
698
|
+
Base64.urlsafe_encode64(cipher.random_iv + cipher.update(plain) + cipher.final, padding: false)
|
699
|
+
end
|
700
|
+
|
701
|
+
|
702
|
+
def self.reveal(token)
|
703
|
+
data = Base64.urlsafe_decode64(token)
|
704
|
+
cipher = OpenSSL::Cipher.new('AES-256-CTR')
|
705
|
+
cipher.decrypt
|
706
|
+
cipher.key = SECRET
|
707
|
+
cipher.iv = data[0...cipher.iv_len]
|
708
|
+
cipher.update(data[cipher.iv_len..-1]) + cipher.final
|
709
|
+
end
|
674
710
|
|
675
711
|
|
676
712
|
class Encryption
|
@@ -872,9 +908,18 @@ module Bitferry
|
|
872
908
|
def execute(*args)
|
873
909
|
cmd = [Rclone.executable] + args
|
874
910
|
cms = cmd.collect(&:shellescape).join(' ')
|
875
|
-
puts cms if Bitferry.verbosity == :verbose
|
911
|
+
$stdout.puts cms if Bitferry.verbosity == :verbose
|
876
912
|
log.info(cms)
|
877
|
-
|
913
|
+
if Bitferry.ui == :gui
|
914
|
+
t = nil
|
915
|
+
Open3.popen2e(*cmd) do |i, oe, thr|
|
916
|
+
while x = oe.gets; $stdout.puts(x) end
|
917
|
+
t = thr
|
918
|
+
end
|
919
|
+
status = t.value
|
920
|
+
else
|
921
|
+
status = Open3.pipeline(cmd).first
|
922
|
+
end
|
878
923
|
raise RuntimeError, "rclone exit code #{status.exitstatus}" unless status.success?
|
879
924
|
status.success?
|
880
925
|
end
|
@@ -1058,8 +1103,9 @@ module Bitferry
|
|
1058
1103
|
def execute(*args, simulate: false, chdir: nil)
|
1059
1104
|
cmd = [Restic.executable] + args
|
1060
1105
|
ENV['RESTIC_PASSWORD'] = password
|
1106
|
+
ENV['RESTIC_PROGRESS_FPS'] = 1.to_s if Bitferry.verbosity == :verbose && Bitferry.ui == :gui
|
1061
1107
|
cms = cmd.collect(&:shellescape).join(' ')
|
1062
|
-
puts cms if Bitferry.verbosity == :verbose
|
1108
|
+
$stdout.puts cms if Bitferry.verbosity == :verbose
|
1063
1109
|
log.info(cms)
|
1064
1110
|
if simulate
|
1065
1111
|
log.info('(simulated)')
|
@@ -1068,7 +1114,16 @@ module Bitferry
|
|
1068
1114
|
wd = Dir.getwd unless chdir.nil?
|
1069
1115
|
begin
|
1070
1116
|
Dir.chdir(chdir) unless chdir.nil?
|
1071
|
-
|
1117
|
+
if Bitferry.ui == :gui
|
1118
|
+
t = nil
|
1119
|
+
Open3.popen2e(*cmd) do |i, oe, thr|
|
1120
|
+
while x = oe.gets; $stdout.puts(x) end
|
1121
|
+
t = thr
|
1122
|
+
end
|
1123
|
+
status = t.value
|
1124
|
+
else
|
1125
|
+
status = Open3.pipeline(cmd).first
|
1126
|
+
end
|
1072
1127
|
raise RuntimeError, "restic exit code #{status.exitstatus}" unless status.success?
|
1073
1128
|
status.success?
|
1074
1129
|
ensure
|
@@ -1147,6 +1202,10 @@ module Bitferry
|
|
1147
1202
|
def show_direction = '-->'
|
1148
1203
|
|
1149
1204
|
|
1205
|
+
alias :source :directory
|
1206
|
+
alias :destination :repository
|
1207
|
+
|
1208
|
+
|
1150
1209
|
def process
|
1151
1210
|
begin
|
1152
1211
|
log.info("processing task #{tag}")
|
@@ -1235,7 +1294,7 @@ module Bitferry
|
|
1235
1294
|
end
|
1236
1295
|
|
1237
1296
|
|
1238
|
-
def exclude_filters = exclude.collect { |x| ['--exclude', x]}.flatten
|
1297
|
+
def exclude_filters = exclude.collect { |x| ['--exclude', x] }.flatten
|
1239
1298
|
|
1240
1299
|
|
1241
1300
|
def show_status = "#{show_operation} #{repository.show_status} #{show_direction} #{directory.show_status} #{show_filters}"
|
@@ -1247,6 +1306,10 @@ module Bitferry
|
|
1247
1306
|
def show_direction = '-->'
|
1248
1307
|
|
1249
1308
|
|
1309
|
+
alias :destination :directory
|
1310
|
+
alias :source :repository
|
1311
|
+
|
1312
|
+
|
1250
1313
|
def externalize
|
1251
1314
|
restic = {
|
1252
1315
|
process: process_options
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitferry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleg A. Khlybov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -39,19 +39,47 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '5.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: seven-zip
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
47
|
+
version: '1.4'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
54
|
+
version: '1.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: archive-zip
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.12'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.12'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: redcarpet
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.6'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.6'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: neatjson
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +94,20 @@ dependencies:
|
|
66
94
|
- - "~>"
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '0.10'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: fxruby
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.6'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.6'
|
69
111
|
- !ruby/object:Gem::Dependency
|
70
112
|
name: clamp
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -84,14 +126,17 @@ description:
|
|
84
126
|
email: fougas@mail.ru
|
85
127
|
executables:
|
86
128
|
- bitferry
|
129
|
+
- bitferryfx
|
87
130
|
extensions: []
|
88
131
|
extra_rdoc_files: []
|
89
132
|
files:
|
90
133
|
- CHANGES.md
|
91
134
|
- README.md
|
92
135
|
- bin/bitferry
|
136
|
+
- bin/bitferryfx
|
93
137
|
- lib/bitferry.rb
|
94
138
|
- lib/bitferry/cli.rb
|
139
|
+
- lib/bitferry/fx.rb
|
95
140
|
homepage: https://github.com/okhlybov/bitferry
|
96
141
|
licenses:
|
97
142
|
- BSD-3-Clause
|