zold-stress 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/bin/zold-stress +12 -11
- data/fixtures/scripts/_head.sh +2 -0
- data/fixtures/scripts/multi-node.sh +27 -17
- data/fixtures/scripts/simple-stress.sh +6 -6
- data/lib/zold/stress/air.rb +9 -6
- data/lib/zold/stress/pmnts.rb +1 -1
- data/lib/zold/stress/pool.rb +1 -1
- data/lib/zold/stress/round.rb +12 -4
- data/lib/zold/stress/stats.rb +3 -1
- data/test/zold/stress/fake_node.rb +1 -0
- data/test/zold/stress/test_air.rb +16 -1
- data/test/zold/stress/test_bin.rb +3 -3
- data/test/zold/stress/test_round.rb +1 -1
- data/test/zold/test__helper.rb +1 -3
- data/zold-stress.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 298a325cf8b0b4dc1f0158e8295111a2564f91cf90734f7b8d3ff9e2bf19efe0
|
4
|
+
data.tar.gz: 463737afba3299a6333e18f2f527f140d338dc73b223b4c49f58aaf044c9aafe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: abada0b527309a1f116fe74de753c96365b092675533608bcf9afbafedf2ae190e9727f3241399c4d6f5552d54f630f19c2830616bb53243daf478243bd64249
|
7
|
+
data.tar.gz: f3466ba90fc620c5340bbb621cd2ba52bd09f23d84451b8c771009f56444b7c025277801df7db4a91ba0c58437ffd39f7f5db4b25927acccbd2d47f531219f88
|
data/.rubocop.yml
CHANGED
data/bin/zold-stress
CHANGED
@@ -44,8 +44,8 @@ Thread.current.name = 'main'
|
|
44
44
|
Encoding.default_external = Encoding::UTF_8
|
45
45
|
Encoding.default_internal = Encoding::UTF_8
|
46
46
|
|
47
|
-
log = Zold::Log::
|
48
|
-
vlog = Zold::Log::
|
47
|
+
log = Zold::Log::REGULAR
|
48
|
+
vlog = Zold::Log::ERRORS
|
49
49
|
|
50
50
|
begin
|
51
51
|
opts = Slop.parse(ARGV, strict: false, suppress_errors: true) do |o|
|
@@ -74,22 +74,19 @@ Available options:"
|
|
74
74
|
"Home directory (default: #{Dir.pwd})",
|
75
75
|
default: Dir.pwd
|
76
76
|
o.string '--network',
|
77
|
-
"The name of the network we work in (default: #{Zold::Wallet::
|
77
|
+
"The name of the network we work in (default: #{Zold::Wallet::MAINET}",
|
78
78
|
required: true,
|
79
|
-
default: Zold::Wallet::
|
79
|
+
default: Zold::Wallet::MAINET
|
80
80
|
o.bool '-h', '--help', 'Show these instructions'
|
81
81
|
o.on '--verbose', 'Enable extra logging information' do
|
82
|
-
log = Zold::Log::
|
83
|
-
vlog = Zold::Log::
|
82
|
+
log = Zold::Log::VERBOSE
|
83
|
+
vlog = Zold::Log::REGULAR
|
84
84
|
end
|
85
85
|
o.on '--no-colors', 'Disable colors in the ouput' do
|
86
86
|
Rainbow.enabled = false
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
log = Zold::Log::Sync.new(log)
|
91
|
-
vlog = Zold::Log::Sync.new(vlog)
|
92
|
-
|
93
90
|
if opts.help?
|
94
91
|
log.info(opts.to_s)
|
95
92
|
exit
|
@@ -112,7 +109,7 @@ Available options:"
|
|
112
109
|
end
|
113
110
|
copies = File.join(zoldata, 'copies')
|
114
111
|
|
115
|
-
stats = Zold::Stress::Stats.new
|
112
|
+
stats = Zold::Stress::Stats.new(log: vlog)
|
116
113
|
summary = Zold::Stress::Summary.new(stats, opts['batch'])
|
117
114
|
air = Zold::Stress::Air.new
|
118
115
|
round = Zold::Stress::Round.new(
|
@@ -137,7 +134,7 @@ Available options:"
|
|
137
134
|
round.pull
|
138
135
|
round.match
|
139
136
|
log.info(summary)
|
140
|
-
|
137
|
+
round.list if (r % 10).zero?
|
141
138
|
end
|
142
139
|
s = Time.now
|
143
140
|
loop do
|
@@ -147,7 +144,11 @@ Available options:"
|
|
147
144
|
round.match
|
148
145
|
log.info(summary)
|
149
146
|
end
|
147
|
+
round.list
|
150
148
|
unless air.fetch.empty?
|
149
|
+
air.fetch.each do |p|
|
150
|
+
log.info(" #{p[:source]} -> #{p[:target]} #{p[:amount]} #{Zold::Age.new(p[:pushed])}")
|
151
|
+
end
|
151
152
|
raise "#{air.fetch.count} payments out of #{stats.total('paid')} are still somewhere, we lost them :("
|
152
153
|
end
|
153
154
|
log.info("Successfully sent and received #{Rainbow(stats.total('arrived')).green} transactions \
|
data/fixtures/scripts/_head.sh
CHANGED
@@ -1,36 +1,47 @@
|
|
1
1
|
#!/bin/bash
|
2
2
|
|
3
3
|
function start_node {
|
4
|
-
port=$
|
4
|
+
port=$1
|
5
5
|
mkdir ${port}
|
6
6
|
cd ${port}
|
7
|
+
zold remote clean
|
7
8
|
zold node --trace --invoice=MULTINODE@ffffffffffffffff \
|
8
9
|
--host=localhost --port=${port} --bind-port=${port} --dump-errors \
|
9
|
-
--no-metronome --halt-code=test --threads=1 --strength=
|
10
|
+
--no-metronome --halt-code=test --threads=1 --strength=3 --pretty=full > log.txt 2>&1 &
|
10
11
|
pid=$!
|
11
12
|
echo ${pid} > pid
|
12
13
|
cd ..
|
13
|
-
wait_for_url http://localhost:${port}/
|
14
|
-
echo ${port}
|
14
|
+
wait_for_url "http://localhost:${port}/"
|
15
15
|
}
|
16
16
|
|
17
|
-
nodes=(
|
17
|
+
nodes=()
|
18
|
+
for i in `seq 1 4`; do
|
19
|
+
port=$(reserve_port)
|
20
|
+
nodes+=($port)
|
21
|
+
start_node $port &
|
22
|
+
done
|
23
|
+
wait
|
24
|
+
|
18
25
|
trap "halt_nodes ${nodes[*]}" EXIT
|
26
|
+
|
19
27
|
for port in ${nodes[@]}; do
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
{
|
29
|
+
cd ${port}
|
30
|
+
for friend in ${nodes[@]}; do
|
31
|
+
if [ "${port}" != "${friend}" ]; then
|
32
|
+
zold remote add localhost ${friend}
|
33
|
+
fi
|
34
|
+
done
|
35
|
+
cd ..
|
36
|
+
} &
|
28
37
|
done
|
38
|
+
wait
|
29
39
|
|
30
40
|
zold remote clean
|
31
41
|
for port in ${nodes[@]}; do
|
32
|
-
zold remote add localhost ${port}
|
42
|
+
zold remote add localhost ${port} &
|
33
43
|
done
|
44
|
+
wait
|
34
45
|
|
35
46
|
zold --public-key=id_rsa.pub create 0000000000000000
|
36
47
|
zold --public-key=id_rsa.pub create abcdabcdabcdabcd
|
@@ -38,6 +49,5 @@ zold pay --private-key=id_rsa 0000000000000000 abcdabcdabcdabcd 4.95 'To test'
|
|
38
49
|
zold push 0000000000000000 --ignore-score-weakness
|
39
50
|
zold remove 0000000000000000
|
40
51
|
|
41
|
-
zold-stress --rounds=
|
42
|
-
|
43
|
-
zold show
|
52
|
+
# zold-stress --rounds=32 --wait=10 --threads=8 --pool=8 --batch=16 --private-key=id_rsa --ignore-score-weakness
|
53
|
+
zold-stress --rounds=4 --wait=10 --threads=${#nodes[@]} --pool=8 --batch=8 --private-key=id_rsa --ignore-score-weakness
|
@@ -1,21 +1,21 @@
|
|
1
1
|
#!/bin/bash
|
2
2
|
|
3
3
|
function start_node {
|
4
|
-
port=$
|
4
|
+
port=$1
|
5
5
|
mkdir ${port}
|
6
6
|
cd ${port}
|
7
7
|
zold node --trace --invoice=SPREADWALLETS@ffffffffffffffff \
|
8
8
|
--host=localhost --port=${port} --bind-port=${port} --dump-errors \
|
9
9
|
--standalone --no-metronome --halt-code=test \
|
10
|
-
--threads=0
|
10
|
+
--threads=0 --verbose --pretty=full 2>&1 &
|
11
11
|
pid=$!
|
12
12
|
echo ${pid} > pid
|
13
13
|
cd ..
|
14
|
-
wait_for_url http://localhost:${port}/
|
15
|
-
echo ${port}
|
14
|
+
wait_for_url "http://localhost:${port}/"
|
16
15
|
}
|
17
16
|
|
18
|
-
port=$(
|
17
|
+
port=$(reserve_port)
|
18
|
+
start_node $port
|
19
19
|
trap "halt_nodes ${port}" EXIT
|
20
20
|
zold remote clean
|
21
21
|
zold remote add localhost ${port}
|
@@ -26,5 +26,5 @@ zold pay --private-key=id_rsa 0000000000000000 abcdabcdabcdabcd 4.95 'To test'
|
|
26
26
|
zold push 0000000000000000
|
27
27
|
zold remove 0000000000000000
|
28
28
|
|
29
|
-
# zold-stress --rounds=
|
29
|
+
# zold-stress --rounds=8 --wait=5 --threads=32 --pool=32 --batch=64 --private-key=id_rsa --verbose
|
30
30
|
zold-stress --rounds=4 --wait=5 --threads=4 --pool=4 --batch=4 --private-key=id_rsa
|
data/lib/zold/stress/air.rb
CHANGED
@@ -32,25 +32,28 @@ module Zold::Stress
|
|
32
32
|
@all = []
|
33
33
|
end
|
34
34
|
|
35
|
-
def fetch
|
36
|
-
@all
|
35
|
+
def fetch(any = false)
|
36
|
+
@all.select { |p| any || p[:arrived].nil? }
|
37
37
|
end
|
38
38
|
|
39
39
|
def add(pmt)
|
40
40
|
@mutex.synchronize do
|
41
|
+
raise "Payment already exists (#{@all.size} total): #{pmt}" if @all.find { |p| p[:details] == pmt[:details] }
|
41
42
|
@all << pmt.merge(pushed: Time.now)
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
|
-
def
|
46
|
+
def pulled(id)
|
46
47
|
@mutex.synchronize do
|
47
|
-
@all.
|
48
|
+
@all.select { |a| a[:target] == id }.each { |a| a[:pulled] = Time.now }
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
|
-
def
|
52
|
+
def arrived(pmt)
|
52
53
|
@mutex.synchronize do
|
53
|
-
@all.
|
54
|
+
found = @all.find { |p| p[:details] == pmt[:details] }
|
55
|
+
raise "Payment doesn't exist (#{@all.size} total): #{pmt}" if found.nil?
|
56
|
+
found[:arrived] = Time.now
|
54
57
|
end
|
55
58
|
end
|
56
59
|
end
|
data/lib/zold/stress/pmnts.rb
CHANGED
@@ -36,7 +36,7 @@ module Zold::Stress
|
|
36
36
|
# Payments to send in a batch.
|
37
37
|
class Pmnts
|
38
38
|
def initialize(pvt:, wallets:, remotes:, stats:, opts:,
|
39
|
-
vlog: Zold::Log::
|
39
|
+
vlog: Zold::Log::NULL, log: Zold::Log::NULL)
|
40
40
|
@pvt = pvt
|
41
41
|
@wallets = wallets
|
42
42
|
@remotes = remotes
|
data/lib/zold/stress/pool.rb
CHANGED
@@ -38,7 +38,7 @@ module Zold::Stress
|
|
38
38
|
# Pool of wallets.
|
39
39
|
class Pool
|
40
40
|
def initialize(wallets:, remotes:, copies:, stats:, opts:,
|
41
|
-
log: Zold::Log::
|
41
|
+
log: Zold::Log::NULL, vlog: Zold::Log::NULL)
|
42
42
|
@wallets = wallets
|
43
43
|
@remotes = remotes
|
44
44
|
@copies = copies
|
data/lib/zold/stress/round.rb
CHANGED
@@ -26,6 +26,7 @@ require 'zold/key'
|
|
26
26
|
require 'zold/id'
|
27
27
|
require 'zold/commands/push'
|
28
28
|
require 'zold/commands/remote'
|
29
|
+
require 'zold/commands/list'
|
29
30
|
require_relative 'stats'
|
30
31
|
require_relative 'pool'
|
31
32
|
require_relative 'pmnts'
|
@@ -39,7 +40,7 @@ module Zold::Stress
|
|
39
40
|
# Full round of stress test
|
40
41
|
class Round
|
41
42
|
def initialize(pvt:, wallets:, remotes:, copies:,
|
42
|
-
stats:, air:, opts:, log: Zold::Log::
|
43
|
+
stats:, air:, opts:, log: Zold::Log::NULL, vlog: Zold::Log::NULL)
|
43
44
|
@pvt = pvt
|
44
45
|
@wallets = wallets
|
45
46
|
@remotes = remotes
|
@@ -51,6 +52,10 @@ module Zold::Stress
|
|
51
52
|
@vlog = vlog
|
52
53
|
end
|
53
54
|
|
55
|
+
def list
|
56
|
+
Zold::List.new(wallets: @wallets, copies: @copies, log: @log).run(['list'] + @opts.arguments)
|
57
|
+
end
|
58
|
+
|
54
59
|
def update
|
55
60
|
start = Time.now
|
56
61
|
cmd = Zold::Remote.new(remotes: @remotes, log: @vlog)
|
@@ -74,6 +79,7 @@ module Zold::Stress
|
|
74
79
|
)
|
75
80
|
pool.rebuild
|
76
81
|
@wallets.all.peach(@opts['threads']) do |id|
|
82
|
+
Thread.current.name = 'prepare-push'
|
77
83
|
@stats.exec('push') do
|
78
84
|
Zold::Push.new(wallets: @wallets, remotes: @remotes, log: @vlog).run(
|
79
85
|
['push', id.to_s, "--network=#{@opts['network']}"] + @opts.arguments
|
@@ -95,6 +101,7 @@ in #{Zold::Age.new(start)}")
|
|
95
101
|
mutex = Mutex.new
|
96
102
|
sources = sent.group_by { |p| p[:source] }
|
97
103
|
sources.peach(@opts['threads']) do |a|
|
104
|
+
Thread.current.name = 'send-push'
|
98
105
|
@stats.exec('push') do
|
99
106
|
Zold::Push.new(wallets: @wallets, remotes: @remotes, log: @vlog).run(
|
100
107
|
['push', a[0].to_s, "--network=#{@opts['network']}"] + @opts.arguments
|
@@ -109,16 +116,17 @@ in #{Zold::Age.new(start)}")
|
|
109
116
|
sent from #{sources.count} wallets, \
|
110
117
|
in #{Zold::Age.new(start)}, #{@air.fetch.count} are now in the air, \
|
111
118
|
#{Zold::Age.new(@air.fetch.map { |a| a[:pushed] }.reverse[0] || Time.now)} is the oldest")
|
112
|
-
@log.debug(
|
119
|
+
@log.debug(sent.map { |p| "#{p[:source]} -> #{p[:target]} #{p[:amount]}" }.join("\n"))
|
113
120
|
end
|
114
121
|
|
115
122
|
def pull
|
116
123
|
start = Time.now
|
117
124
|
targets = @air.fetch.group_by { |p| p[:target] }.map { |a| a[0] }
|
118
125
|
targets.peach(@opts['threads']) do |id|
|
126
|
+
Thread.current.name = "pull-#{id}"
|
119
127
|
@stats.exec('pull') do
|
120
128
|
Zold::Pull.new(wallets: @wallets, remotes: @remotes, copies: @copies, log: @vlog).run(
|
121
|
-
['pull', id.to_s, "--network=#{@opts['network']}"] + @opts.arguments
|
129
|
+
['pull', id.to_s, "--network=#{@opts['network']}", '--skip-propagate'] + @opts.arguments
|
122
130
|
)
|
123
131
|
end
|
124
132
|
@air.pulled(id)
|
@@ -134,11 +142,11 @@ after the pull of #{targets.count} in #{Zold::Age.new(start)}")
|
|
134
142
|
next unless @wallets.acq(p[:target], &:exists?)
|
135
143
|
t = @wallets.acq(p[:target], &:txns).find { |x| x.details == p[:details] && x.bnf == p[:source] }
|
136
144
|
next if t.nil?
|
145
|
+
@air.arrived(p)
|
137
146
|
@stats.put('arrived', p[:pulled] - p[:pushed])
|
138
147
|
total += 1
|
139
148
|
@log.debug("#{p[:amount]} arrived from #{p[:source]} to #{p[:target]} \
|
140
149
|
in txn ##{t.id} in #{Zold::Age.new(p[:start])}: #{t.details}")
|
141
|
-
@air.delete(p)
|
142
150
|
end
|
143
151
|
@log.info("#{total} payments just arrived, #{@air.fetch.count} still in the air")
|
144
152
|
end
|
data/lib/zold/stress/stats.rb
CHANGED
@@ -33,9 +33,10 @@ require 'zold/age'
|
|
33
33
|
module Zold::Stress
|
34
34
|
# Stats
|
35
35
|
class Stats
|
36
|
-
def initialize
|
36
|
+
def initialize(log: Zold::Log::NULL)
|
37
37
|
@history = {}
|
38
38
|
@mutex = Mutex.new
|
39
|
+
@log = log
|
39
40
|
end
|
40
41
|
|
41
42
|
def exists?(metric)
|
@@ -78,6 +79,7 @@ module Zold::Stress
|
|
78
79
|
put(metric + '_ok', Time.now - start)
|
79
80
|
rescue StandardError => ex
|
80
81
|
put(metric + '_error', Time.now - start)
|
82
|
+
@log.error(Backtrace.new(ex))
|
81
83
|
raise ex unless swallow
|
82
84
|
ensure
|
83
85
|
put(metric, Time.now - start)
|
@@ -44,6 +44,7 @@ module Zold::Stress
|
|
44
44
|
RandomPort::Pool::SINGLETON.acquire do |port|
|
45
45
|
Dir.mktmpdir do |home|
|
46
46
|
thread = Thread.start do
|
47
|
+
Thread.current.name = 'fake_node'
|
47
48
|
Zold::VerboseThread.new(@log).run do
|
48
49
|
node = Zold::Node.new(
|
49
50
|
wallets: Zold::Wallets.new(home),
|
@@ -36,7 +36,22 @@ class AirTest < Minitest::Test
|
|
36
36
|
air.fetch.each do |p|
|
37
37
|
assert_equal(pmt[:details], p[:details])
|
38
38
|
end
|
39
|
-
air.
|
39
|
+
air.pulled(air.fetch[0][:target])
|
40
|
+
assert_equal(1, air.fetch.count)
|
41
|
+
air.arrived(air.fetch[0])
|
40
42
|
assert_equal(0, air.fetch.count)
|
41
43
|
end
|
44
|
+
|
45
|
+
def test_adds_and_removes_many
|
46
|
+
air = Zold::Stress::Air.new
|
47
|
+
5.times do |i|
|
48
|
+
pmt = { start: Time.now, source: Zold::Id::ROOT, target: Zold::Id::ROOT, details: i.to_s }
|
49
|
+
air.add(pmt)
|
50
|
+
assert_equal(i + 1, air.fetch.count)
|
51
|
+
end
|
52
|
+
air.pulled(air.fetch[0][:target])
|
53
|
+
assert_equal(5, air.fetch.count)
|
54
|
+
air.arrived(air.fetch[0])
|
55
|
+
assert_equal(4, air.fetch.count)
|
56
|
+
end
|
42
57
|
end
|
@@ -36,7 +36,7 @@ class TestBin < Minitest::Test
|
|
36
36
|
Dir.new('fixtures/scripts').select { |f| f =~ /\.sh$/ && !f.start_with?('_') }.each do |f|
|
37
37
|
define_method("test_#{f.gsub(/\.sh$/, '').gsub(/[^a-z]/, '_')}") do
|
38
38
|
start = Time.now
|
39
|
-
test_log.
|
39
|
+
test_log.info("\n\n#{f} running...")
|
40
40
|
Dir.mktmpdir do |dir|
|
41
41
|
FileUtils.cp('fixtures/id_rsa.pub', dir)
|
42
42
|
FileUtils.cp('fixtures/id_rsa', dir)
|
@@ -49,7 +49,7 @@ class TestBin < Minitest::Test
|
|
49
49
|
stdin.close
|
50
50
|
until stdout.eof?
|
51
51
|
line = stdout.gets
|
52
|
-
test_log.
|
52
|
+
test_log.info(line)
|
53
53
|
out << line
|
54
54
|
end
|
55
55
|
code = thr.value.to_i
|
@@ -57,7 +57,7 @@ class TestBin < Minitest::Test
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
60
|
-
test_log.
|
60
|
+
test_log.info("\n\n#{f} done in #{Zold::Age.new(start)}")
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -46,7 +46,7 @@ require_relative '../../../lib/zold/stress/air'
|
|
46
46
|
|
47
47
|
class StressTest < Minitest::Test
|
48
48
|
def test_runs_a_few_full_cycles
|
49
|
-
Zold::Stress::FakeNode.new(Zold::Log::
|
49
|
+
Zold::Stress::FakeNode.new(Zold::Log::NULL).exec do |port|
|
50
50
|
Dir.mktmpdir do |home|
|
51
51
|
remotes = Zold::Remotes.new(file: File.join(home, 'remotes'), network: 'test')
|
52
52
|
remotes.clean
|
data/test/zold/test__helper.rb
CHANGED
@@ -20,8 +20,6 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
21
|
# SOFTWARE.
|
22
22
|
|
23
|
-
STDOUT.sync = true
|
24
|
-
|
25
23
|
ENV['RACK_ENV'] = 'test'
|
26
24
|
|
27
25
|
# require 'simplecov'
|
@@ -38,7 +36,7 @@ module Minitest
|
|
38
36
|
class Test
|
39
37
|
def test_log
|
40
38
|
require 'zold/log'
|
41
|
-
@test_log ||=
|
39
|
+
@test_log ||= ENV['TEST_QUIET_LOG'] ? Zold::Log::NULL : Zold::Log::VERBOSE
|
42
40
|
end
|
43
41
|
|
44
42
|
def test_opts(*argv)
|
data/zold-stress.gemspec
CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.rubygems_version = '2.2'
|
28
28
|
s.required_ruby_version = '>=2.3'
|
29
29
|
s.name = 'zold-stress'
|
30
|
-
s.version = '0.5.
|
30
|
+
s.version = '0.5.2'
|
31
31
|
s.license = 'MIT'
|
32
32
|
s.summary = 'Zold stress test'
|
33
33
|
s.description = 'Stress testing toolkit for Zold network'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zold-stress
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-11-
|
11
|
+
date: 2018-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: backtrace
|