zold 0.5 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -2
- data/bin/zold +34 -48
- data/features/cli.feature +1 -1
- data/features/step_definitions/steps.rb +1 -3
- data/features/support/env.rb +2 -0
- data/fixtures/scripts/push-and-pull.sh +6 -4
- data/lib/zold/amount.rb +17 -3
- data/lib/zold/commands/create.rb +9 -6
- data/lib/zold/commands/fetch.rb +11 -21
- data/lib/zold/commands/node.rb +7 -9
- data/lib/zold/commands/pay.rb +9 -6
- data/lib/zold/commands/propagate.rb +4 -5
- data/lib/zold/commands/push.rb +10 -5
- data/lib/zold/commands/remote.rb +22 -24
- data/lib/zold/commands/show.rb +1 -2
- data/lib/zold/commands/taxes.rb +154 -0
- data/lib/zold/http.rb +1 -3
- data/lib/zold/key.rb +1 -3
- data/lib/zold/node/entrance.rb +8 -3
- data/lib/zold/node/farm.rb +8 -6
- data/lib/zold/node/front.rb +0 -1
- data/lib/zold/patch.rb +1 -1
- data/lib/zold/remotes.rb +4 -0
- data/lib/zold/score.rb +85 -10
- data/lib/zold/signature.rb +7 -7
- data/lib/zold/tax.rb +79 -0
- data/lib/zold/txn.rb +12 -7
- data/lib/zold/version.rb +1 -1
- data/lib/zold/wallet.rb +2 -2
- data/test/commands/test_create.rb +3 -4
- data/test/commands/test_diff.rb +2 -3
- data/test/commands/test_merge.rb +4 -6
- data/test/commands/test_pay.rb +7 -5
- data/test/commands/test_remote.rb +5 -3
- data/test/commands/test_taxes.rb +66 -0
- data/test/node/fake_node.rb +1 -0
- data/test/node/test_farm.rb +2 -1
- data/test/node/test_front.rb +1 -0
- data/test/test__helper.rb +2 -0
- data/test/test_remotes.rb +0 -1
- data/test/test_score.rb +40 -21
- data/test/test_signature.rb +6 -3
- data/test/test_tax.rb +53 -0
- data/test/test_txn.rb +46 -0
- data/test/test_wallet.rb +2 -2
- data/test/test_zold.rb +1 -1
- data/wp/.gitignore +6 -0
- data/wp/wp.tex +38 -0
- data/zold.gemspec +1 -3
- metadata +12 -2
@@ -61,21 +61,20 @@ Available options:"
|
|
61
61
|
wallet.txns.select { |t| t.amount.negative? }.each do |t|
|
62
62
|
target = @wallets.find(t.bnf)
|
63
63
|
unless target.exists?
|
64
|
-
@log.debug("#{t.amount
|
64
|
+
@log.debug("#{t.amount * -1} to #{t.bnf}: wallet is absent")
|
65
65
|
next
|
66
66
|
end
|
67
67
|
next if target.has?(t.id, me)
|
68
68
|
unless Prefixes.new(target).valid?(t.prefix)
|
69
|
-
@log.info("#{t.amount
|
69
|
+
@log.info("#{t.amount * -1} to #{t.bnf}: wrong prefix")
|
70
70
|
next
|
71
71
|
end
|
72
72
|
target.add(t.inverse(me))
|
73
|
-
@log.info("#{t.amount
|
73
|
+
@log.info("#{t.amount * -1} arrived to #{t.bnf}: #{t.details}")
|
74
74
|
modified << t.id
|
75
75
|
end
|
76
76
|
modified.uniq!
|
77
|
-
@log.debug("Wallet #{me} propagated successfully,
|
78
|
-
#{modified.count} wallets affected")
|
77
|
+
@log.debug("Wallet #{me} propagated successfully, #{modified.count} wallets affected")
|
79
78
|
modified
|
80
79
|
end
|
81
80
|
end
|
data/lib/zold/commands/push.rb
CHANGED
@@ -69,12 +69,17 @@ Available options:"
|
|
69
69
|
next
|
70
70
|
end
|
71
71
|
json = JSON.parse(response.body)['score']
|
72
|
-
score = Score.
|
73
|
-
Time.parse(json['time']), json['host'],
|
74
|
-
json['port'], json['suffixes']
|
75
|
-
)
|
72
|
+
score = Score.parse_json(json)
|
76
73
|
unless score.valid?
|
77
|
-
@log.error("#{uri} invalid score")
|
74
|
+
@log.error("#{uri} invalid score: #{score}")
|
75
|
+
next
|
76
|
+
end
|
77
|
+
if score.expired?
|
78
|
+
@log.error("#{uri} expired score: #{score}")
|
79
|
+
next
|
80
|
+
end
|
81
|
+
if score.strength < Score::STRENGTH
|
82
|
+
@log.error("#{uri} score is too weak")
|
78
83
|
next
|
79
84
|
end
|
80
85
|
@log.info("#{uri} accepted: #{Rainbow(score.value).green}")
|
data/lib/zold/commands/remote.rb
CHANGED
@@ -50,9 +50,9 @@ Available commands:
|
|
50
50
|
Remove all registered remote nodes
|
51
51
|
#{Rainbow('remote reset').green}
|
52
52
|
Restore it back to the default list of nodes
|
53
|
-
#{Rainbow('remote add').green} host port
|
53
|
+
#{Rainbow('remote add').green} host [port]
|
54
54
|
Add a new remote node
|
55
|
-
#{Rainbow('remote remove').green} host port
|
55
|
+
#{Rainbow('remote remove').green} host [port]
|
56
56
|
Remove the remote node
|
57
57
|
#{Rainbow('remote update').green}
|
58
58
|
Check each registered remote node for availability
|
@@ -62,7 +62,7 @@ Available options:"
|
|
62
62
|
default: false
|
63
63
|
o.bool '--help', 'Print instructions'
|
64
64
|
end
|
65
|
-
command =
|
65
|
+
command = opts.arguments[0]
|
66
66
|
case command
|
67
67
|
when 'show'
|
68
68
|
show
|
@@ -71,9 +71,9 @@ Available options:"
|
|
71
71
|
when 'reset'
|
72
72
|
reset
|
73
73
|
when 'add'
|
74
|
-
add(opts.arguments[1], opts.arguments[2].to_i)
|
74
|
+
add(opts.arguments[1], opts.arguments[2] ? opts.arguments[2].to_i : Remotes::PORT)
|
75
75
|
when 'remove'
|
76
|
-
remove(opts.arguments[1], opts.arguments[2].to_i)
|
76
|
+
remove(opts.arguments[1], opts.arguments[2] ? opts.arguments[2].to_i : Remotes::PORT)
|
77
77
|
when 'update'
|
78
78
|
update(opts)
|
79
79
|
update(opts, false)
|
@@ -117,9 +117,7 @@ Available options:"
|
|
117
117
|
res = Http.new(uri).get
|
118
118
|
unless res.code == '200'
|
119
119
|
@remotes.remove(r[:host], r[:port])
|
120
|
-
@log.info(
|
121
|
-
"#{Rainbow(r[:host]).red} #{res.code} \"#{res.message}\" #{uri}"
|
122
|
-
)
|
120
|
+
@log.info("#{Rainbow(r[:host]).red} #{res.code} \"#{res.message}\" #{uri}")
|
123
121
|
next
|
124
122
|
end
|
125
123
|
begin
|
@@ -129,39 +127,39 @@ Available options:"
|
|
129
127
|
@log.info("#{Rainbow(r[:host]).red} \"#{e.message}\": #{res.body}")
|
130
128
|
next
|
131
129
|
end
|
132
|
-
score = Score.
|
133
|
-
Time.parse(json['score']['time']), r[:host],
|
134
|
-
r[:port], json['score']['suffixes']
|
135
|
-
)
|
130
|
+
score = Score.parse_json(json['score'])
|
136
131
|
unless score.valid?
|
137
132
|
remove(r[:host], r[:port])
|
138
133
|
@log.info("#{Rainbow(r[:host]).red} invalid score")
|
139
134
|
next
|
140
135
|
end
|
136
|
+
if score.expired?
|
137
|
+
remove(r[:host], r[:port])
|
138
|
+
@log.info("#{Rainbow(r[:host]).red} expired score")
|
139
|
+
next
|
140
|
+
end
|
141
141
|
if score.strength < Score::STRENGTH && !opts['ignore-score-weakness']
|
142
142
|
remove(r[:host], r[:port])
|
143
|
-
@log.info(
|
144
|
-
"#{Rainbow(r[:host]).red} score too weak: #{score.strength}"
|
145
|
-
)
|
143
|
+
@log.info("#{Rainbow(r[:host]).red} score too weak: #{score.strength}")
|
146
144
|
next
|
147
145
|
end
|
148
|
-
|
146
|
+
if r[:host] != score.host || r[:port] != score.port
|
147
|
+
@remotes.remove(r[:host], r[:port])
|
148
|
+
@remotes.add(score.host, score.port)
|
149
|
+
@log.info("#{r[:host]}:#{r[:port]} renamed to #{score.host}:#{score.port}")
|
150
|
+
end
|
151
|
+
@remotes.rescore(score.host, score.port, score.value)
|
149
152
|
if deep
|
150
153
|
json['all'].each do |s|
|
151
|
-
unless @remotes.exists?(s['host'], s['port'])
|
152
|
-
add(s['host'], s['port'])
|
153
|
-
end
|
154
|
+
add(s['host'], s['port']) unless @remotes.exists?(s['host'], s['port'])
|
154
155
|
end
|
155
156
|
end
|
156
|
-
@log.info("#{r[:host]}:#{r[:port]}: #{Rainbow(score.value).green}
|
157
|
-
(v.#{json['version']})")
|
157
|
+
@log.info("#{r[:host]}:#{r[:port]}: #{Rainbow(score.value).green} (v.#{json['version']})")
|
158
158
|
end
|
159
159
|
total = @remotes.all.size
|
160
160
|
if total.zero?
|
161
161
|
@log.debug("The list of remotes is #{Rainbow('empty').red}!")
|
162
|
-
@log.debug(
|
163
|
-
"Run 'zold remote add b1.zold.io 80` and then `zold update`"
|
164
|
-
)
|
162
|
+
@log.debug("Run 'zold remote add b1.zold.io` and then `zold update`")
|
165
163
|
else
|
166
164
|
@log.debug("There are #{total} known remotes")
|
167
165
|
end
|
data/lib/zold/commands/show.rb
CHANGED
@@ -60,8 +60,7 @@ Available options:"
|
|
60
60
|
def show(wallet, _)
|
61
61
|
balance = wallet.balance
|
62
62
|
wallet.txns.each do |t|
|
63
|
-
@log.info(
|
64
|
-
#{t.amount} #{t.bnf} #{t.details}")
|
63
|
+
@log.info(t.to_text)
|
65
64
|
end
|
66
65
|
@log.info("The balance of #{wallet}: #{balance}")
|
67
66
|
balance
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# Copyright (c) 2018 Yegor Bugayenko
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
11
|
+
# copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
# SOFTWARE.
|
20
|
+
|
21
|
+
require 'slop'
|
22
|
+
require 'json'
|
23
|
+
require_relative 'pay'
|
24
|
+
require_relative '../log'
|
25
|
+
require_relative '../score'
|
26
|
+
require_relative '../id'
|
27
|
+
require_relative '../tax'
|
28
|
+
require_relative '../http'
|
29
|
+
|
30
|
+
# TAXES command.
|
31
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
32
|
+
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
33
|
+
# License:: MIT
|
34
|
+
module Zold
|
35
|
+
# Taxes command
|
36
|
+
class Taxes
|
37
|
+
def initialize(wallets:, remotes:, log: Log::Quiet.new)
|
38
|
+
@wallets = wallets
|
39
|
+
@remotes = remotes
|
40
|
+
@log = log
|
41
|
+
end
|
42
|
+
|
43
|
+
def run(args = [])
|
44
|
+
opts = Slop.parse(args, help: true) do |o|
|
45
|
+
o.banner = "Usage: zold taxes command [options]
|
46
|
+
Available commands:
|
47
|
+
#{Rainbow('taxes pay').green} wallet
|
48
|
+
Pay taxes for the given wallet
|
49
|
+
#{Rainbow('taxes show').green}
|
50
|
+
Show taxes status for the given wallet
|
51
|
+
#{Rainbow('taxes debt').green}
|
52
|
+
Show current debt
|
53
|
+
Available options:"
|
54
|
+
o.string '--private-key',
|
55
|
+
'The location of RSA private key (default: ~/.ssh/id_rsa)',
|
56
|
+
require: true,
|
57
|
+
default: '~/.ssh/id_rsa'
|
58
|
+
o.bool '--help', 'Print instructions'
|
59
|
+
end
|
60
|
+
command = opts.arguments[0]
|
61
|
+
case command
|
62
|
+
when 'show'
|
63
|
+
raise 'At least one wallet ID is required' unless opts.arguments[1]
|
64
|
+
opts.arguments[1..-1].each do |id|
|
65
|
+
show(@wallets.find(Id.new(id), opts))
|
66
|
+
end
|
67
|
+
when 'debt'
|
68
|
+
raise 'At least one wallet ID is required' unless opts.arguments[1]
|
69
|
+
opts.arguments[1..-1].each do |id|
|
70
|
+
debt(@wallets.find(Id.new(id), opts))
|
71
|
+
end
|
72
|
+
when 'pay'
|
73
|
+
raise 'At least one wallet ID is required' unless opts.arguments[1]
|
74
|
+
opts.arguments[1..-1].each do |id|
|
75
|
+
pay(@wallets.find(Id.new(id)), opts)
|
76
|
+
end
|
77
|
+
else
|
78
|
+
@log.info(opts.to_s)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def pay(wallet, _)
|
83
|
+
raise 'The wallet is absent' unless wallet.exists?
|
84
|
+
tax = Tax.new(wallet)
|
85
|
+
debt = tax.debt
|
86
|
+
@log.debug("The current debt is #{debt} (#{debt.to_i} zents)")
|
87
|
+
if debt <= Tax::TRIAL
|
88
|
+
@log.debug("No need to pay taxes yet, until the debt is less than #{Tax::TRIAL} (#{Tax::TRIAL.to_i} zents)")
|
89
|
+
return
|
90
|
+
end
|
91
|
+
top = top_scores
|
92
|
+
while debt > Amount::ZERO
|
93
|
+
raise 'No acceptable remote nodes, try later' if top.empty?
|
94
|
+
best = top.shift
|
95
|
+
txn = tax.pay(Zold::Key.new(file: opts['private-key']), best)
|
96
|
+
wallet.add(txn)
|
97
|
+
debt -= txn.amount
|
98
|
+
@log.info("#{txn.amount} of taxes paid to #{txn.bnf}, #{debt} left to pay")
|
99
|
+
end
|
100
|
+
@log.info('The wallet is in good standing, all taxes paid')
|
101
|
+
end
|
102
|
+
|
103
|
+
def debt(wallet, _)
|
104
|
+
raise 'The wallet is absent' unless wallet.exists?
|
105
|
+
tax = Tax.new(wallet)
|
106
|
+
@log.info(tax.debt)
|
107
|
+
end
|
108
|
+
|
109
|
+
def show(_, _)
|
110
|
+
raise 'Not implemented yet'
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def top_scores
|
116
|
+
best = []
|
117
|
+
@remotes.all.each do |r|
|
118
|
+
uri = URI(r[:home])
|
119
|
+
name = "#{r[:host]}:#{r[:port]}"
|
120
|
+
res = Http.new(uri).get
|
121
|
+
unless res.code == '200'
|
122
|
+
@log.info("#{name}: #{Rainbow(res.code).red} \"#{res.message}\" at #{uri}")
|
123
|
+
next
|
124
|
+
end
|
125
|
+
begin
|
126
|
+
json = JSON.parse(res.body)
|
127
|
+
rescue JSON::ParserError => e
|
128
|
+
@log.info("#{name}: #{Rainbow('broken').red} JSON \"#{e.message}\": #{res.body}")
|
129
|
+
next
|
130
|
+
end
|
131
|
+
score = Score.parse_json(json['score'])
|
132
|
+
unless score.valid?
|
133
|
+
@log.info("#{name}: #{Rainbow('invalid').red} score")
|
134
|
+
next
|
135
|
+
end
|
136
|
+
if score.expired?
|
137
|
+
@log.info("#{name}: #{Rainbow('expired').red} score")
|
138
|
+
next
|
139
|
+
end
|
140
|
+
if score.strength < Score::STRENGTH
|
141
|
+
@log.info("#{name} score #{Rainbow(score.value).red} is too weak (<#{Score::STRENGTH})")
|
142
|
+
next
|
143
|
+
end
|
144
|
+
if score.value < Tax::MIN_SCORE
|
145
|
+
@log.info("#{name} score #{Rainbow(score.value).red} is too small (<#{Tax::MIN_SCORE})")
|
146
|
+
next
|
147
|
+
end
|
148
|
+
@log.info("#{score.host}:#{score.port}: #{Rainbow(score.value).green}")
|
149
|
+
best << score
|
150
|
+
end
|
151
|
+
best.sort_by(&:value).reverse
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/zold/http.rb
CHANGED
@@ -66,9 +66,7 @@ module Zold
|
|
66
66
|
'User-Agent': "Zold #{VERSION}",
|
67
67
|
'Connection': 'close'
|
68
68
|
}
|
69
|
-
if @score.valid? && @score.value >= 3
|
70
|
-
headers[SCORE_HEADER] = score.reduced(4).to_s
|
71
|
-
end
|
69
|
+
headers[SCORE_HEADER] = score.reduced(4).to_s if @score.valid? && @score.value >= 3 && !score.expired?
|
72
70
|
headers
|
73
71
|
end
|
74
72
|
end
|
data/lib/zold/key.rb
CHANGED
@@ -34,9 +34,7 @@ module Zold
|
|
34
34
|
@body = lambda do
|
35
35
|
unless file.nil?
|
36
36
|
path = File.expand_path(file)
|
37
|
-
unless File.exist?(path)
|
38
|
-
raise "Can't find RSA key at #{file} (#{path})"
|
39
|
-
end
|
37
|
+
raise "Can't find RSA key at #{file} (#{path})" unless File.exist?(path)
|
40
38
|
return File.read(path)
|
41
39
|
end
|
42
40
|
unless text.nil?
|
data/lib/zold/node/entrance.rb
CHANGED
@@ -22,6 +22,7 @@ require 'concurrent'
|
|
22
22
|
require_relative '../log'
|
23
23
|
require_relative '../remotes'
|
24
24
|
require_relative '../copies'
|
25
|
+
require_relative '../tax'
|
25
26
|
require_relative '../commands/merge'
|
26
27
|
require_relative '../commands/fetch'
|
27
28
|
require_relative '../commands/push'
|
@@ -52,15 +53,19 @@ module Zold
|
|
52
53
|
def push_unsafe(id, body)
|
53
54
|
copies = Copies.new(File.join(@copies, id.to_s))
|
54
55
|
copies.add(body, 'remote', Remotes::PORT, 0)
|
55
|
-
|
56
|
+
Fetch.new(
|
56
57
|
remotes: @remotes, copies: copies.root, log: @log
|
57
58
|
).run([id.to_s, "--ignore-node=#{@address}"])
|
58
|
-
modified =
|
59
|
+
modified = Merge.new(
|
59
60
|
wallets: @wallets, copies: copies.root, log: @log
|
60
61
|
).run([id.to_s])
|
62
|
+
debt = Tax.new(@wallets.find(id)).debt
|
63
|
+
if debt > Tax::TRIAL
|
64
|
+
raise "Taxes are not paid, the debt is #{debt} (#{debt.to_i} zents), won't promote the wallet"
|
65
|
+
end
|
61
66
|
copies.remove('remote', Remotes::PORT)
|
62
67
|
modified.each do |m|
|
63
|
-
|
68
|
+
Push.new(
|
64
69
|
wallets: @wallets, remotes: @remotes, log: @log
|
65
70
|
).run([m.to_s])
|
66
71
|
end
|
data/lib/zold/node/farm.rb
CHANGED
@@ -30,8 +30,9 @@ module Zold
|
|
30
30
|
# Farm
|
31
31
|
class Farm
|
32
32
|
attr_reader :best
|
33
|
-
def initialize(log: Log::Quiet.new)
|
33
|
+
def initialize(invoice, log: Log::Quiet.new)
|
34
34
|
@log = log
|
35
|
+
@invoice = invoice
|
35
36
|
@scores = []
|
36
37
|
@threads = []
|
37
38
|
@best = []
|
@@ -50,7 +51,7 @@ module Zold
|
|
50
51
|
def start(host, port, strength: 8, threads: 8)
|
51
52
|
@log.debug('Zero-threads farm won\'t score anything!') if threads.zero?
|
52
53
|
@scores = Queue.new
|
53
|
-
first = Score.new(Time.now, host, port, strength: strength)
|
54
|
+
first = Score.new(Time.now, host, port, @invoice, strength: strength)
|
54
55
|
@best = [first]
|
55
56
|
@scores << first
|
56
57
|
@threads = (1..threads).map do |t|
|
@@ -64,12 +65,13 @@ module Zold
|
|
64
65
|
@best << s
|
65
66
|
after = @best.map(&:value).max
|
66
67
|
@best.reject! { |b| b.value < after }
|
67
|
-
if before != after
|
68
|
-
@log.debug("#{Thread.current.name}: best is #{@best[0]}")
|
69
|
-
end
|
68
|
+
@log.debug("#{Thread.current.name}: best is #{@best[0]}") if before != after
|
70
69
|
end
|
71
70
|
if @scores.length < 4
|
72
|
-
@scores << Score.new(
|
71
|
+
@scores << Score.new(
|
72
|
+
Time.now, host, port, @invoice,
|
73
|
+
strength: strength
|
74
|
+
)
|
73
75
|
end
|
74
76
|
@scores << s.next
|
75
77
|
end
|
data/lib/zold/node/front.rb
CHANGED
@@ -60,7 +60,6 @@ module Zold
|
|
60
60
|
if request.env[Http::SCORE_HEADER] && !settings.remotes.empty?
|
61
61
|
s = Score.parse(request.env[Http::SCORE_HEADER])
|
62
62
|
error(400, 'The score is invalid') unless s.valid?
|
63
|
-
error(400, 'The score is weak') if s.strength < Score::STRENGTH
|
64
63
|
settings.remotes.add(s.host, s.port) if s.value > 3
|
65
64
|
end
|
66
65
|
end
|
data/lib/zold/patch.rb
CHANGED
data/lib/zold/remotes.rb
CHANGED
@@ -67,6 +67,7 @@ module Zold
|
|
67
67
|
raise 'Port has to be of type Integer' unless port.is_a?(Integer)
|
68
68
|
raise 'Port can\'t be negative' if port < 0
|
69
69
|
raise 'Port can\'t be over 65536' if port > 0xffff
|
70
|
+
raise "#{host}:#{port} alread exists" if exists?(host, port)
|
70
71
|
list = load
|
71
72
|
list << { host: host.downcase, port: port, score: 0 }
|
72
73
|
list.uniq! { |r| "#{r[:host]}:#{r[:port]}" }
|
@@ -75,6 +76,7 @@ module Zold
|
|
75
76
|
|
76
77
|
def remove(host, port = Remotes::PORT)
|
77
78
|
raise 'Port has to be of type Integer' unless port.is_a?(Integer)
|
79
|
+
raise "#{host}:#{port} is absent" unless exists?(host, port)
|
78
80
|
list = load
|
79
81
|
list.reject! { |r| r[:host] == host.downcase && r[:port] == port }
|
80
82
|
save(list)
|
@@ -82,11 +84,13 @@ module Zold
|
|
82
84
|
|
83
85
|
def score(host, port = Remotes::PORT)
|
84
86
|
raise 'Port has to be of type Integer' unless port.is_a?(Integer)
|
87
|
+
raise "#{host}:#{port} is absent" unless exists?(host, port)
|
85
88
|
load.find { |r| r[:host] == host.downcase && r[:port] == port }[:score]
|
86
89
|
end
|
87
90
|
|
88
91
|
def rescore(host, port, score)
|
89
92
|
raise 'Port has to be of type Integer' unless port.is_a?(Integer)
|
93
|
+
raise "#{host}:#{port} is absent" unless exists?(host, port)
|
90
94
|
list = load
|
91
95
|
list.find do |r|
|
92
96
|
r[:host] == host.downcase && r[:port] == port
|