zold 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/bin/zold +9 -6
  4. data/features/gem_package.feature +1 -1
  5. data/features/step_definitions/steps.rb +2 -2
  6. data/fixtures/merge/into-no-wallet/assert.rb +1 -1
  7. data/fixtures/merge/random-expenses/assert.rb +1 -1
  8. data/fixtures/merge/simple-case/assert.rb +1 -1
  9. data/lib/zold/age.rb +16 -8
  10. data/lib/zold/amount.rb +4 -2
  11. data/lib/zold/cached_wallets.rb +5 -6
  12. data/lib/zold/commands/clean.rb +15 -8
  13. data/lib/zold/commands/diff.rb +3 -3
  14. data/lib/zold/commands/fetch.rb +4 -4
  15. data/lib/zold/commands/list.rb +5 -4
  16. data/lib/zold/commands/merge.rb +2 -1
  17. data/lib/zold/commands/node.rb +14 -12
  18. data/lib/zold/commands/propagate.rb +2 -2
  19. data/lib/zold/commands/push.rb +3 -3
  20. data/lib/zold/commands/remove.rb +4 -1
  21. data/lib/zold/commands/routines/reconnect.rb +1 -0
  22. data/lib/zold/copies.rb +13 -14
  23. data/lib/zold/dir_items.rb +44 -0
  24. data/lib/zold/head.rb +1 -1
  25. data/lib/zold/key.rb +2 -2
  26. data/lib/zold/log.rb +2 -0
  27. data/lib/zold/node/async_entrance.rb +38 -28
  28. data/lib/zold/node/entrance.rb +4 -11
  29. data/lib/zold/node/farm.rb +9 -9
  30. data/lib/zold/node/front.rb +40 -25
  31. data/lib/zold/node/nodup_entrance.rb +4 -4
  32. data/lib/zold/node/safe_entrance.rb +2 -2
  33. data/lib/zold/node/spread_entrance.rb +5 -9
  34. data/lib/zold/node/sync_entrance.rb +2 -23
  35. data/lib/zold/patch.rb +2 -2
  36. data/lib/zold/remotes.rb +10 -6
  37. data/lib/zold/sync_wallets.rb +3 -22
  38. data/lib/zold/tree_wallets.rb +11 -6
  39. data/lib/zold/txns.rb +1 -1
  40. data/lib/zold/version.rb +1 -1
  41. data/lib/zold/wallet.rb +14 -5
  42. data/lib/zold/wallets.rb +5 -4
  43. data/test/commands/routines/test_spread.rb +1 -1
  44. data/test/commands/test_alias.rb +4 -4
  45. data/test/commands/test_clean.rb +14 -1
  46. data/test/commands/test_create.rb +2 -2
  47. data/test/commands/test_diff.rb +5 -5
  48. data/test/commands/test_fetch.rb +2 -2
  49. data/test/commands/test_merge.rb +19 -19
  50. data/test/commands/test_node.rb +1 -1
  51. data/test/commands/test_pay.rb +7 -6
  52. data/test/commands/test_propagate.rb +2 -1
  53. data/test/commands/test_pull.rb +1 -1
  54. data/test/commands/test_push.rb +1 -1
  55. data/test/commands/test_remove.rb +57 -0
  56. data/test/commands/test_taxes.rb +1 -1
  57. data/test/fake_home.rb +11 -8
  58. data/test/node/fake_node.rb +2 -2
  59. data/test/node/test_async_entrance.rb +24 -8
  60. data/test/node/test_emission.rb +2 -2
  61. data/test/node/test_entrance.rb +8 -6
  62. data/test/node/test_farm.rb +1 -1
  63. data/test/node/test_front.rb +42 -33
  64. data/test/node/test_nodup_entrance.rb +2 -2
  65. data/test/node/test_safe_entrance.rb +5 -5
  66. data/test/node/test_spread_entrance.rb +3 -3
  67. data/test/node/test_sync_entrance.rb +1 -1
  68. data/test/test__helper.rb +3 -29
  69. data/test/test_cached_wallets.rb +1 -1
  70. data/test/test_copies.rb +4 -2
  71. data/test/test_dir_items.rb +88 -0
  72. data/test/test_key.rb +2 -2
  73. data/test/test_log.rb +38 -0
  74. data/test/test_patch.rb +11 -11
  75. data/test/test_prefixes.rb +1 -1
  76. data/test/test_remotes.rb +12 -6
  77. data/test/test_sync_wallets.rb +6 -6
  78. data/test/test_tax.rb +4 -4
  79. data/test/test_tree_wallets.rb +16 -2
  80. data/test/test_wallet.rb +26 -26
  81. data/test/test_wallets.rb +5 -2
  82. data/test/test_zold.rb +2 -2
  83. data/test/upgrades/test_protocol_up.rb +1 -1
  84. data/upgrades/move_wallets_into_tree.rb +1 -1
  85. data/upgrades/protocol_up.rb +3 -3
  86. data/upgrades/rename_foreign_wallets.rb +1 -1
  87. data/zold.gemspec +27 -25
  88. metadata +91 -56
@@ -54,13 +54,13 @@ module Zold
54
54
  raise 'Id can\'t be nil' if id.nil?
55
55
  raise 'Id must be of type Id' unless id.is_a?(Id)
56
56
  raise 'Body can\'t be nil' if body.nil?
57
- Tempfile.open(['', Wallet::EXTENSION]) do |f|
58
- File.write(f, body)
57
+ Tempfile.open(['', Wallet::EXT]) do |f|
58
+ IO.write(f, body)
59
59
  wallet = Wallet.new(f.path)
60
60
  wallet.refurbish
61
- after = File.read(wallet.path)
61
+ after = IO.read(wallet.path)
62
62
  before = @wallets.find(id) do |w|
63
- w.exists? ? File.read(w.path).to_s : ''
63
+ w.exists? ? IO.read(w.path).to_s : ''
64
64
  end
65
65
  if before == after
66
66
  @log.info(
@@ -58,8 +58,8 @@ module Zold
58
58
  raise 'Id can\'t be nil' if id.nil?
59
59
  raise 'Id must be of type Id' unless id.is_a?(Id)
60
60
  raise 'Body can\'t be nil' if body.nil?
61
- Tempfile.open(['', Wallet::EXTENSION]) do |f|
62
- File.write(f, body)
61
+ Tempfile.open(['', Wallet::EXT]) do |f|
62
+ IO.write(f, body)
63
63
  wallet = Wallet.new(f.path)
64
64
  wallet.refurbish
65
65
  unless wallet.protocol == Zold::PROTOCOL
@@ -21,6 +21,7 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  require 'concurrent'
24
+ require 'concurrent/set'
24
25
  require 'tempfile'
25
26
  require_relative 'emission'
26
27
  require_relative '../log'
@@ -40,17 +41,10 @@ module Zold
40
41
  # The entrance
41
42
  class SpreadEntrance
42
43
  def initialize(entrance, wallets, remotes, address, log: Log::Quiet.new, ignore_score_weakeness: false)
43
- raise 'Entrance can\'t be nil' if entrance.nil?
44
44
  @entrance = entrance
45
- raise 'Wallets can\'t be nil' if wallets.nil?
46
- raise 'Wallets must implement the contract of Wallets: method #find is required' unless wallets.respond_to?(:find)
47
45
  @wallets = wallets
48
- raise 'Remotes can\'t be nil' if remotes.nil?
49
- raise 'Remotes must be of type Remotes' unless remotes.is_a?(Remotes)
50
46
  @remotes = remotes
51
- raise 'Address can\'t be nil' if address.nil?
52
47
  @address = address
53
- raise 'Log can\'t be nil' if log.nil?
54
48
  @log = log
55
49
  @ignore_score_weakeness = ignore_score_weakeness
56
50
  end
@@ -64,7 +58,7 @@ module Zold
64
58
 
65
59
  def start
66
60
  @entrance.start do
67
- @seen = Set.new
61
+ @seen = Concurrent::Set.new
68
62
  @modified = Queue.new
69
63
  @push = Thread.start do
70
64
  Thread.current.abort_on_exception = true
@@ -95,13 +89,15 @@ module Zold
95
89
  end
96
90
  end
97
91
 
92
+ # This method is thread-safe
98
93
  def push(id, body)
99
94
  mods = @entrance.push(id, body)
95
+ return mods if @remotes.all.empty?
100
96
  (mods + [id]).each do |m|
101
97
  next if @seen.include?(m)
102
98
  @seen << m
103
99
  @modified.push(m)
104
- @log.debug("Push scheduled for #{m}, queue size is #{@modified.size}")
100
+ @log.debug("Spread-push scheduled for #{m}, queue size is #{@modified.size}")
105
101
  end
106
102
  mods
107
103
  end
@@ -21,6 +21,7 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  require 'concurrent'
24
+ require 'futex'
24
25
  require_relative '../log'
25
26
  require_relative '../id'
26
27
  require_relative '../verbose_thread'
@@ -33,12 +34,9 @@ module Zold
33
34
  # The entrance that makes sure only one thread works with a wallet
34
35
  class SyncEntrance
35
36
  def initialize(entrance, dir, timeout: 30, log: Log::Quiet.new)
36
- raise 'Entrance can\'t be nil' if entrance.nil?
37
37
  @entrance = entrance
38
- raise 'Dir can\'t be nil' if dir.nil?
39
38
  @dir = dir
40
39
  @timeout = timeout
41
- raise 'Log can\'t be nil' if log.nil?
42
40
  @log = log
43
41
  end
44
42
 
@@ -54,26 +52,7 @@ module Zold
54
52
 
55
53
  # Always returns an array with a single ID of the pushed wallet
56
54
  def push(id, body)
57
- f = File.join(@dir, "#{id}.lock")
58
- FileUtils.mkdir_p(File.dirname(f))
59
- File.open(f, File::RDWR | File::CREAT) do |lock|
60
- start = Time.now
61
- cycles = 0
62
- loop do
63
- break if lock.flock(File::LOCK_EX | File::LOCK_NB)
64
- sleep 0.1
65
- cycles += 1
66
- delay = Time.now - start
67
- if delay > @timeout
68
- raise "##{Process.pid}/#{Thread.current.name} can't get exclusive access to the wallet #{id}/e \
69
- because of the lock at #{lock.path}: #{File.read(lock)}"
70
- end
71
- if (cycles % 20).zero? && delay > 10
72
- @log.info("##{Process.pid}/#{Thread.current.name} still waiting for \
73
- exclusive access to #{id}/e, #{delay.round}s already")
74
- end
75
- end
76
- File.write(lock, "##{Process.pid}/#{Thread.current.name}/#{Time.now.utc.iso8601}")
55
+ Futex.new(File.join(@dir, id), log: @log).open do
77
56
  @entrance.push(id, body)
78
57
  end
79
58
  end
data/lib/zold/patch.rb CHANGED
@@ -119,7 +119,7 @@ among #{payer.txns.count} transactions: #{txn.to_text}")
119
119
  def save(file, overwrite: false)
120
120
  raise 'You have to join at least one wallet in' if @id.nil?
121
121
  before = ''
122
- before = File.read(file) if File.exist?(file)
122
+ before = IO.read(file) if File.exist?(file)
123
123
  wallet = Wallet.new(file)
124
124
  wallet.init(@id, @key, overwrite: overwrite, network: @network)
125
125
  File.open(file, 'a') do |f|
@@ -128,7 +128,7 @@ among #{payer.txns.count} transactions: #{txn.to_text}")
128
128
  end
129
129
  end
130
130
  wallet.refurbish
131
- after = File.read(file)
131
+ after = IO.read(file)
132
132
  before != after
133
133
  end
134
134
  end
data/lib/zold/remotes.rb CHANGED
@@ -25,6 +25,7 @@ require 'csv'
25
25
  require 'uri'
26
26
  require 'net/http'
27
27
  require 'time'
28
+ require 'futex'
28
29
  require 'fileutils'
29
30
  require 'backtrace'
30
31
  require_relative 'age'
@@ -52,10 +53,13 @@ module Zold
52
53
  attribute :file, Types::Strict::String
53
54
  attribute :network, Types::Strict::String.optional.default('test')
54
55
  attribute :timeout, Types::Strict::Integer.optional.default(16)
55
- attribute :mutex, Types::Object.optional.default(Mutex.new)
56
56
 
57
57
  # Empty, for standalone mode
58
- class Empty < Remotes
58
+ class Empty
59
+ def initialize
60
+ # Nothing to init here
61
+ end
62
+
59
63
  def all
60
64
  []
61
65
  end
@@ -131,7 +135,7 @@ module Zold
131
135
  end
132
136
 
133
137
  def defaults
134
- other = Remotes.new(file: File.join(File.dirname(__FILE__), '../../resources/remotes'))
138
+ other = Remotes.new(file: File.expand_path(File.join(File.dirname(__FILE__), '../../resources/remotes')))
135
139
  other.all.each do |r|
136
140
  add(r[:host], r[:port])
137
141
  end
@@ -220,13 +224,13 @@ module Zold
220
224
  end
221
225
 
222
226
  def mtime
223
- File.mtime(file)
227
+ File.exist?(file) ? File.mtime(file) : Time.now
224
228
  end
225
229
 
226
230
  private
227
231
 
228
232
  def modify
229
- mutex.synchronize do
233
+ Futex.new(file).open do
230
234
  save(yield(load))
231
235
  end
232
236
  end
@@ -261,7 +265,7 @@ module Zold
261
265
 
262
266
  def save(list)
263
267
  FileUtils.mkdir_p(File.dirname(file))
264
- File.write(
268
+ IO.write(
265
269
  file,
266
270
  list.uniq { |r| "#{r[:host]}:#{r[:port]}" }.map do |r|
267
271
  [
@@ -20,6 +20,7 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
+ require 'futex'
23
24
  require_relative 'log'
24
25
 
25
26
  # Sync collection of wallets.
@@ -29,9 +30,8 @@ require_relative 'log'
29
30
  module Zold
30
31
  # Synchronized collection of wallets
31
32
  class SyncWallets
32
- def initialize(wallets, dir = Dir.tmpdir, timeout: 30, log: Log::Quiet.new)
33
+ def initialize(wallets, timeout: 30, log: Log::Quiet.new)
33
34
  @wallets = wallets
34
- @dir = dir
35
35
  @log = log
36
36
  @timeout = timeout
37
37
  end
@@ -50,26 +50,7 @@ module Zold
50
50
 
51
51
  def find(id)
52
52
  @wallets.find(id) do |wallet|
53
- f = File.join(@dir, id)
54
- FileUtils.mkdir_p(File.dirname(f))
55
- File.open(f, File::RDWR | File::CREAT) do |lock|
56
- start = Time.now
57
- cycles = 0
58
- loop do
59
- break if lock.flock(File::LOCK_EX | File::LOCK_NB)
60
- sleep 0.1
61
- cycles += 1
62
- delay = (Time.now - start).round(2)
63
- if delay > @timeout
64
- raise "##{Process.pid}/#{Thread.current.name} can't get exclusive access to the wallet #{id} \
65
- because of the lock at #{lock.path}, after #{delay}s of waiting: #{File.read(lock)}"
66
- end
67
- if (cycles % 20).zero? && delay > 10
68
- @log.info("##{Process.pid}/#{Thread.current.name} still waiting for \
69
- exclusive access to #{id}, #{delay.round}s already: #{File.read(lock)}")
70
- end
71
- end
72
- File.write(lock, "##{Process.pid}/#{Thread.current.name}")
53
+ Futex.new(wallet.path, log: @log).open do
73
54
  yield wallet
74
55
  end
75
56
  end
@@ -22,6 +22,7 @@
22
22
  require 'pathname'
23
23
  require_relative 'id'
24
24
  require_relative 'wallet'
25
+ require_relative 'dir_items'
25
26
 
26
27
  # The local collection of wallets.
27
28
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -45,19 +46,23 @@ module Zold
45
46
 
46
47
  # Returns the list of their IDs (as plain text)
47
48
  def all
48
- Dir.glob("#{path}/**/*#{Wallet::EXTENSION}").select do |f|
49
- basename = File.basename(f, Wallet::EXTENSION)
50
- File.file?(f) &&
51
- !File.directory?(f) &&
49
+ DirItems.new(path).fetch.select do |f|
50
+ next unless f.end_with?(Wallet::EXT)
51
+ basename = File.basename(f, Wallet::EXT)
52
+ file = File.join(path, f)
53
+ File.file?(file) &&
54
+ !File.directory?(file) &&
52
55
  basename =~ /^[0-9a-fA-F]{16}$/ &&
53
56
  Id.new(basename).to_s == basename
54
- end.map { |w| Id.new(File.basename(w, Wallet::EXTENSION)) }
57
+ end.map { |w| Id.new(File.basename(w, Wallet::EXT)) }
55
58
  end
56
59
 
57
60
  def find(id)
58
61
  raise 'Id can\'t be nil' if id.nil?
59
62
  raise 'Id must be of type Id' unless id.is_a?(Id)
60
- yield Zold::Wallet.new(File.join(path, (id.to_s.split('', 5).take(4) + [id.to_s]).join('/')))
63
+ yield Wallet.new(
64
+ File.join(path, (id.to_s.split('', 5).take(4) + [id.to_s]).join('/') + Wallet::EXT)
65
+ )
61
66
  end
62
67
  end
63
68
  end
data/lib/zold/txns.rb CHANGED
@@ -39,7 +39,7 @@ module Zold
39
39
 
40
40
  def fetch
41
41
  raise "Wallet file '#{@file}' is absent" unless File.exist?(@file)
42
- lines = File.read(@file).split(/\n/)
42
+ lines = IO.read(@file).split(/\n/)
43
43
  raise "Not enough lines in #{@file}, just #{lines.count}" if lines.count < 4
44
44
  lines.drop(5)
45
45
  .each_with_index
data/lib/zold/version.rb CHANGED
@@ -25,6 +25,6 @@
25
25
  # Copyright:: Copyright (c) 2018 Yegor Bugayenko
26
26
  # License:: MIT
27
27
  module Zold
28
- VERSION = '0.15.0'
28
+ VERSION = '0.16.0'
29
29
  PROTOCOL = 2
30
30
  end
data/lib/zold/wallet.rb CHANGED
@@ -27,6 +27,7 @@ require_relative 'key'
27
27
  require_relative 'id'
28
28
  require_relative 'txn'
29
29
  require_relative 'tax'
30
+ require_relative 'copies'
30
31
  require_relative 'amount'
31
32
  require_relative 'hexnum'
32
33
  require_relative 'signature'
@@ -50,10 +51,13 @@ module Zold
50
51
  MAIN_NETWORK = 'zold'
51
52
 
52
53
  # The extension of the wallet files
53
- EXTENSION = '.z'
54
+ EXT = '.z'
54
55
 
55
56
  def initialize(file)
56
- @file = File.absolute_path(File.extname(file).empty? ? "#{file}#{EXTENSION}" : file)
57
+ unless file.end_with?(Wallet::EXT, Copies::EXT)
58
+ raise "Wallet file must end with #{Wallet::EXT} or #{Copies::EXT}: #{file}"
59
+ end
60
+ @file = File.absolute_path(file)
57
61
  @txns = Txns::Cached.new(Txns.new(@file))
58
62
  @head = Head::Cached.new(Head.new(@file))
59
63
  end
@@ -90,7 +94,7 @@ module Zold
90
94
  raise "File '#{@file}' already exists" if File.exist?(@file) && !overwrite
91
95
  raise "Invalid network name '#{network}'" unless network =~ /^[a-z]{4,16}$/
92
96
  FileUtils.mkdir_p(File.dirname(@file))
93
- File.write(@file, "#{network}\n#{PROTOCOL}\n#{id}\n#{pubkey.to_pub}\n\n")
97
+ IO.write(@file, "#{network}\n#{PROTOCOL}\n#{id}\n#{pubkey.to_pub}\n\n")
94
98
  @txns.flush
95
99
  @head.flush
96
100
  end
@@ -173,7 +177,7 @@ module Zold
173
177
  end
174
178
 
175
179
  def digest
176
- OpenSSL::Digest::SHA256.new(File.read(@file)).hexdigest
180
+ OpenSSL::Digest::SHA256.new(IO.read(@file)).hexdigest
177
181
  end
178
182
 
179
183
  # Age of wallet in hours
@@ -187,13 +191,18 @@ module Zold
187
191
  end
188
192
 
189
193
  def refurbish
190
- File.write(
194
+ IO.write(
191
195
  @file,
192
196
  "#{network}\n#{protocol}\n#{id}\n#{key.to_pub}\n\n#{txns.map { |t| t.to_s + "\n" }.join}"
193
197
  )
194
198
  @txns.flush
195
199
  end
196
200
 
201
+ def flush
202
+ @head.flush
203
+ @txns.flush
204
+ end
205
+
197
206
  private
198
207
 
199
208
  def max
data/lib/zold/wallets.rb CHANGED
@@ -22,6 +22,7 @@
22
22
  require 'pathname'
23
23
  require_relative 'id'
24
24
  require_relative 'wallet'
25
+ require_relative 'dir_items'
25
26
 
26
27
  # The local collection of wallets.
27
28
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -50,20 +51,20 @@ module Zold
50
51
 
51
52
  # Returns the list of their IDs (as plain text)
52
53
  def all
53
- Dir.new(path).select do |f|
54
+ DirItems.new(path).fetch(recursive: false).select do |f|
54
55
  file = File.join(@dir, f)
55
- basename = File.basename(f, Wallet::EXTENSION)
56
+ basename = File.basename(f, Wallet::EXT)
56
57
  File.file?(file) &&
57
58
  !File.directory?(file) &&
58
59
  basename =~ /^[0-9a-fA-F]{16}$/ &&
59
60
  Id.new(basename).to_s == basename
60
- end.map { |w| Id.new(File.basename(w, Wallet::EXTENSION)) }
61
+ end.map { |w| Id.new(File.basename(w, Wallet::EXT)) }
61
62
  end
62
63
 
63
64
  def find(id)
64
65
  raise 'Id can\'t be nil' if id.nil?
65
66
  raise "Id must be of type Id, #{id.class.name} instead" unless id.is_a?(Id)
66
- yield Zold::Wallet.new(File.join(path, id.to_s))
67
+ yield Wallet.new(File.join(path, id.to_s + Wallet::EXT))
67
68
  end
68
69
  end
69
70
  end
@@ -34,7 +34,7 @@ require_relative '../../../lib/zold/node/entrance.rb'
34
34
  # License:: MIT
35
35
  class TestSpread < Minitest::Test
36
36
  def test_spread_wallets
37
- FakeHome.new.run do |home|
37
+ FakeHome.new(log: test_log).run do |home|
38
38
  5.times { home.create_wallet }
39
39
  opts = {
40
40
  'routine-immediately' => true
@@ -11,7 +11,7 @@ class TestAlias < Minitest::Test
11
11
  # The syntax is already documented in the alias command in the help.
12
12
  def test_set_writes_alias_to_the_alias_file
13
13
  skip
14
- FakeHome.new.run do |home|
14
+ FakeHome.new(log: test_log).run do |home|
15
15
  wallet = home.create_wallet
16
16
  Zold::Alias.new(wallets: home.wallets, log: test_log).run(%W[set #{wallet.id} my-alias])
17
17
  assert_equal read_alias_file(home), %W[my-alias #{wallet.id}]
@@ -23,7 +23,7 @@ class TestAlias < Minitest::Test
23
23
  # The syntax is already documented in the alias command in the help.
24
24
  def test_remove_removes_the_alias_from_the_alias_file
25
25
  skip
26
- FakeHome.new.run do |home|
26
+ FakeHome.new(log: test_log).run do |home|
27
27
  wallet = home.create_wallet
28
28
  cmd = Zold::Alias.new(wallets: home.wallets, log: test_log)
29
29
  cmd.run(%W[set #{wallet.id} my-alias])
@@ -38,7 +38,7 @@ class TestAlias < Minitest::Test
38
38
  # The syntax is already documented in the alias command in the help.
39
39
  def test_show_prints_out_the_aliased_wallet_id
40
40
  skip
41
- FakeHome.new.run do |home|
41
+ FakeHome.new(log: test_log).run do |home|
42
42
  wallet = home.create_wallet
43
43
  cmd = Zold::Alias.new(wallets: home.wallets, log: test_log)
44
44
  cmd.run(%W[set #{wallet.id} my-alias])
@@ -51,6 +51,6 @@ class TestAlias < Minitest::Test
51
51
  private
52
52
 
53
53
  def read_alias_file(home)
54
- File.read(File.join(home.dir, 'aliases')).split(' ')
54
+ IO.read(File.join(home.dir, 'aliases')).split(' ')
55
55
  end
56
56
  end