file-digests 0.0.32 → 0.0.37

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/file-digests +14 -0
  3. data/lib/file-digests.rb +161 -97
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd4c4c18335dad84ab6622b5dd3f46c5edfd513d4655dfc014cf39a6816dd834
4
- data.tar.gz: df06f84f85f20229aaea1afa3725702e438c972b4cbbc87b064a98ee8315cd51
3
+ metadata.gz: 27e992f2a4849569d6c87e53807ebdc53676b3e47ca2c1efd2799927fd16d0c7
4
+ data.tar.gz: 9e292709b7978d906b0423a980cc72b23f8a44c665f070d71f30953ccdc59256
5
5
  SHA512:
6
- metadata.gz: a1ec44bf23a675c462a32171ae86683f6e48718aae2bb9be00b72286cbdbe87e47f04be3aa9c3f41a9e40e99ee58d3e8a0500fecd89f3413e88b3d03eb42ff57
7
- data.tar.gz: 35c548835a8c987bff317d91fcfd71abc69db8660bd30543ebea5022b93b89d9814b42e64a7d168eacef77c085ba6107f618757cb6ad047183f78155f5b4e9ba
6
+ metadata.gz: c80f844d16255d9437c8dd012eff010df23f5a14d87336bf987b34e913a12fa0932fd85e18760884c63d10f882041afecf77b0b354555cef62aadc98de5ba091
7
+ data.tar.gz: f5f5c8309b8921f034edeb36643cab34dff52e4091cd5508462294a48e79002dfdbdf09a126988449d1775d207651579ca19ffb7cd169038fe71067dabb5b8af
@@ -1,5 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # Copyright 2020 Stanislav Senotrusov <stan@senotrusov.com>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
3
17
  require 'file-digests'
4
18
 
5
19
  FileDigests.run_cli_utility
@@ -1,3 +1,17 @@
1
+ # Copyright 2020 Stanislav Senotrusov <stan@senotrusov.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
1
15
  require "date"
2
16
  require "digest"
3
17
  require "fileutils"
@@ -8,7 +22,8 @@ require "set"
8
22
  require "sqlite3"
9
23
 
10
24
  class FileDigests
11
- DIGEST_ALGORITHMS=["BLAKE2b512", "SHA3-256", "SHA512-256"]
25
+ VERSION = Gem.loaded_specs["file-digests"]&.version&.to_s
26
+ DIGEST_ALGORITHMS = ["BLAKE2b512", "SHA3-256", "SHA512-256"]
12
27
  LEGACY_DIGEST_ALGORITHMS = ["SHA512", "SHA256"]
13
28
 
14
29
  def self.canonical_digest_algorithm_name(string)
@@ -75,7 +90,12 @@ class FileDigests
75
90
  options[:quiet] = true
76
91
  end
77
92
 
78
- opts.on("-t", "--test", "Perform only the test, do not modify the digest database.") do
93
+ opts.on(
94
+ "-t", "--test",
95
+ "Perform a test to verify directory contents.",
96
+ "Compare actual files with the stored digests, check if any files are missing.",
97
+ "Digest database will not be modified."
98
+ ) do
79
99
  options[:test_only] = true
80
100
  end
81
101
 
@@ -96,6 +116,7 @@ class FileDigests
96
116
 
97
117
  def initialize files_path, digest_database_path, options = {}
98
118
  @options = options
119
+ @user_input_wait_time = 0
99
120
 
100
121
  initialize_paths files_path, digest_database_path
101
122
  initialize_database
@@ -139,8 +160,6 @@ class FileDigests
139
160
  @db.results_as_hash = true
140
161
  @db.busy_timeout = 5000
141
162
 
142
- file_digests_gem_version = Gem.loaded_specs["file-digests"]&.version&.to_s
143
-
144
163
  execute "PRAGMA encoding = 'UTF-8'"
145
164
  execute "PRAGMA locking_mode = 'EXCLUSIVE'"
146
165
  execute "PRAGMA journal_mode = 'WAL'"
@@ -155,14 +174,13 @@ class FileDigests
155
174
  execute "CREATE TABLE metadata (
156
175
  key TEXT NOT NULL PRIMARY KEY,
157
176
  value TEXT)"
158
- execute "CREATE UNIQUE INDEX metadata_key ON metadata(key)"
159
177
  metadata_table_was_created = true
160
178
  end
161
179
 
162
180
  prepare_method :set_metadata_query, "INSERT INTO metadata (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value=excluded.value"
163
181
  prepare_method :get_metadata_query, "SELECT value FROM metadata WHERE key = ?"
164
182
 
165
- set_metadata("metadata_table_created_by_gem_version", file_digests_gem_version) if file_digests_gem_version && metadata_table_was_created
183
+ set_metadata("metadata_table_created_by_gem_version", FileDigests::VERSION) if FileDigests::VERSION && metadata_table_was_created
166
184
 
167
185
  # Heuristic to detect database version 1 (metadata was not stored back then)
168
186
  unless get_metadata("database_version")
@@ -179,20 +197,19 @@ class FileDigests
179
197
  digest TEXT NOT NULL,
180
198
  digest_check_time TEXT NOT NULL)"
181
199
  execute "CREATE UNIQUE INDEX digests_filename ON digests(filename)"
182
- set_metadata("digests_table_created_by_gem_version", file_digests_gem_version) if file_digests_gem_version
200
+ execute "CREATE INDEX digests_digest ON digests(digest)"
201
+ set_metadata("digests_table_created_by_gem_version", FileDigests::VERSION) if FileDigests::VERSION
183
202
  end
184
203
 
185
- prepare_method :insert, "INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))"
186
- prepare_method :find_by_filename_query, "SELECT id, mtime, digest FROM digests WHERE filename = ?"
187
- prepare_method :touch_digest_check_time, "UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?"
188
- prepare_method :update_mtime_and_digest, "UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?"
189
- prepare_method :update_mtime, "UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?"
190
- prepare_method :delete_by_filename, "DELETE FROM digests WHERE filename = ?"
191
- prepare_method :query_duplicates, "SELECT digest, filename FROM digests WHERE digest IN (SELECT digest FROM digests GROUP BY digest HAVING count(*) > 1) ORDER BY digest, filename;"
192
- prepare_method :update_digest_to_new_digest, "UPDATE digests SET digest = ? WHERE digest = ?"
204
+ prepare_method :digests_insert, "INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))"
205
+ prepare_method :digests_find_by_filename_query, "SELECT id, mtime, digest FROM digests WHERE filename = ?"
206
+ prepare_method :digests_touch_check_time, "UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?"
207
+ prepare_method :digests_update_mtime_and_digest, "UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?"
208
+ prepare_method :digests_update_mtime, "UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?"
209
+ prepare_method :digests_select_duplicates, "SELECT digest, filename FROM digests WHERE digest IN (SELECT digest FROM digests GROUP BY digest HAVING count(*) > 1) ORDER BY digest, filename;"
193
210
 
194
211
  unless get_metadata("database_version")
195
- set_metadata "database_version", "2"
212
+ set_metadata "database_version", "3"
196
213
  end
197
214
 
198
215
  # Convert database from 1st to 2nd version
@@ -207,79 +224,110 @@ class FileDigests
207
224
  end
208
225
  end
209
226
 
210
- if get_metadata("database_version") != "2"
211
- STDERR.puts "This version of file-digests (#{file_digests_gem_version || "unknown"}) is only compartible with the database version 2. Current database version is #{get_metadata("database_version")}. To use this database, please install appropriate version if file-digest."
212
- raise "Incompatible database version"
227
+ if get_metadata("database_version") == "2"
228
+ execute "CREATE INDEX digests_digest ON digests(digest)"
229
+ set_metadata "database_version", "3"
213
230
  end
231
+
232
+ check_if_database_is_at_certain_version "3"
233
+
234
+ create_temporary_tables
214
235
  end
215
236
  end
216
237
 
217
- def perform_check
218
- perhaps_transaction(@new_digest_algorithm, :exclusive) do
219
- @counters = {good: 0, updated: 0, new: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
220
- @new_files = {}
221
- @new_digests = {}
238
+ def create_temporary_tables
239
+ execute "CREATE TEMPORARY TABLE new_files (
240
+ filename TEXT NOT NULL PRIMARY KEY,
241
+ digest TEXT NOT NULL)"
242
+ execute "CREATE INDEX new_files_digest ON new_files(digest)"
243
+
244
+ prepare_method :new_files_insert, "INSERT INTO new_files (filename, digest) VALUES (?, ?)"
245
+ prepare_method :new_files_count_query, "SELECT count(*) FROM new_files"
246
+
247
+ execute "CREATE TEMPORARY TABLE missing_files (
248
+ filename TEXT NOT NULL PRIMARY KEY,
249
+ digest TEXT NOT NULL)"
250
+ execute "CREATE INDEX missing_files_digest ON missing_files(digest)"
251
+
252
+ execute "INSERT INTO missing_files (filename, digest) SELECT filename, digest FROM digests"
253
+
254
+ prepare_method :missing_files_delete, "DELETE FROM missing_files WHERE filename = ?"
255
+ prepare_method :missing_files_delete_renamed_files, "DELETE FROM missing_files WHERE digest IN (SELECT digest FROM new_files)"
256
+ prepare_method :missing_files_select_all_filenames, "SELECT filename FROM missing_files ORDER BY filename"
257
+ prepare_method :missing_files_delete_all, "DELETE FROM missing_files"
258
+ prepare_method :missing_files_count_query, "SELECT count(*) FROM missing_files"
259
+
260
+ prepare_method :digests_delete_renamed_files, "DELETE FROM digests WHERE filename IN (SELECT filename FROM missing_files WHERE digest IN (SELECT digest FROM new_files))"
261
+ prepare_method :digests_delete_all_missing_files, "DELETE FROM digests WHERE filename IN (SELECT filename FROM missing_files)"
262
+
263
+ execute "CREATE TEMPORARY TABLE new_digests (
264
+ filename TEXT NOT NULL PRIMARY KEY,
265
+ digest TEXT NOT NULL)"
266
+
267
+ prepare_method :new_digests_insert, "INSERT INTO new_digests (filename, digest) VALUES (?, ?)"
268
+ prepare_method :digests_update_digests_to_new_digests, "INSERT INTO digests (filename, digest, digest_check_time) SELECT filename, digest, false FROM new_digests WHERE true ON CONFLICT (filename) DO UPDATE SET digest=excluded.digest"
269
+ end
222
270
 
223
- @missing_files = Hash[@db.prepare("SELECT filename, digest FROM digests").execute!]
271
+ def perform_check
272
+ measure_time do
273
+ perhaps_transaction(@new_digest_algorithm, :exclusive) do
274
+ @counters = {good: 0, updated: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
224
275
 
225
- measure_time do
226
276
  walk_files do |filename|
227
277
  process_file filename
228
278
  end
229
- end
230
279
 
231
- nested_transaction do
232
- puts "Tracking renames..." if @options[:verbose]
233
- track_renames
234
- end
280
+ nested_transaction do
281
+ puts "Tracking renames..." if @options[:verbose]
282
+ track_renames
283
+ end
235
284
 
236
- if any_missing_files?
237
- if any_exceptions?
238
- STDERR.puts "Due to previously occurred errors, database cleanup from missing files will be skipped this time."
239
- else
240
- print_missing_files
241
- if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database"))
242
- nested_transaction do
243
- puts "Removing missing files..." if @options[:verbose]
244
- remove_missing_files
285
+ if any_missing_files?
286
+ if any_exceptions?
287
+ STDERR.puts "Due to previously occurred errors, missing files will not removed from the database."
288
+ else
289
+ print_missing_files
290
+ if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database"))
291
+ nested_transaction do
292
+ puts "Removing missing files..." if @options[:verbose]
293
+ remove_missing_files
294
+ end
245
295
  end
246
296
  end
247
297
  end
248
- end
249
298
 
250
- if @new_digest_algorithm && !@options[:test_only]
251
- if any_missing_files? || any_likely_damaged? || any_exceptions?
252
- STDERR.puts "ERROR: New digest algorithm will not be in effect until there are files that are missing, likely damaged, or processed with an exception."
253
- else
254
- puts "Updating database to a new digest algorithm..." if @options[:verbose]
255
- @new_digests.each do |old_digest, new_digest|
256
- update_digest_to_new_digest new_digest, old_digest
299
+ if @new_digest_algorithm && !@options[:test_only]
300
+ if any_missing_files? || any_likely_damaged? || any_exceptions?
301
+ STDERR.puts "ERROR: New digest algorithm will not be in effect until there are files that are missing, likely damaged, or processed with an exception."
302
+ else
303
+ puts "Updating database to a new digest algorithm..." if @options[:verbose]
304
+ digests_update_digests_to_new_digests
305
+ set_metadata "digest_algorithm", @new_digest_algorithm
306
+ puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}"
257
307
  end
258
- set_metadata "digest_algorithm", @new_digest_algorithm
259
- puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}"
260
308
  end
261
- end
262
309
 
263
- if any_likely_damaged? || any_exceptions?
264
- STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
265
- end
310
+ if any_likely_damaged? || any_exceptions?
311
+ STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
312
+ end
266
313
 
267
- set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
314
+ set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
268
315
 
269
- print_counters
270
- end
271
-
272
- puts "Performing database maintenance..." if @options[:verbose]
273
- execute "PRAGMA optimize"
274
- execute "VACUUM"
275
- execute "PRAGMA wal_checkpoint(TRUNCATE)"
316
+ print_counters
317
+ end
318
+
319
+ puts "Performing database maintenance..." if @options[:verbose]
320
+ execute "PRAGMA optimize"
321
+ execute "VACUUM"
322
+ execute "PRAGMA wal_checkpoint(TRUNCATE)"
276
323
 
277
- hide_database_files
324
+ hide_database_files
325
+ end
278
326
  end
279
327
 
280
328
  def show_duplicates
281
329
  current_digest = nil
282
- query_duplicates.each do |found|
330
+ digests_select_duplicates.each do |found|
283
331
  if current_digest != found["digest"]
284
332
  puts "" if current_digest
285
333
  current_digest = found["digest"]
@@ -311,9 +359,10 @@ class FileDigests
311
359
 
312
360
  normalized_filename = filename.delete_prefix("#{@files_path.to_s}/").encode("utf-8", universal_newline: true).unicode_normalize(:nfkc)
313
361
  mtime_string = time_to_database stat.mtime
314
- digest = get_file_digest(filename)
362
+ digest, new_digest = get_file_digest(filename)
315
363
 
316
364
  nested_transaction do
365
+ new_digests_insert(normalized_filename, new_digest) if new_digest
317
366
  process_file_indeed normalized_filename, mtime_string, digest
318
367
  end
319
368
 
@@ -331,15 +380,15 @@ class FileDigests
331
380
  end
332
381
 
333
382
  def process_previously_seen_file found, filename, mtime, digest
334
- @missing_files.delete(filename)
383
+ missing_files_delete filename
335
384
  if found["digest"] == digest
336
385
  @counters[:good] += 1
337
386
  puts "GOOD: #{filename}" if @options[:verbose]
338
387
  unless @options[:test_only]
339
388
  if found["mtime"] == mtime
340
- touch_digest_check_time found["id"]
389
+ digests_touch_check_time found["id"]
341
390
  else
342
- update_mtime mtime, found["id"]
391
+ digests_update_mtime mtime, found["id"]
343
392
  end
344
393
  end
345
394
  else
@@ -350,18 +399,17 @@ class FileDigests
350
399
  @counters[:updated] += 1
351
400
  puts "UPDATED#{" (FATE ACCEPTED)" if found["mtime"] == mtime && @options[:accept_fate]}: #{filename}" unless @options[:quiet]
352
401
  unless @options[:test_only]
353
- update_mtime_and_digest mtime, digest, found["id"]
402
+ digests_update_mtime_and_digest mtime, digest, found["id"]
354
403
  end
355
404
  end
356
405
  end
357
406
  end
358
407
 
359
408
  def process_new_file filename, mtime, digest
360
- @counters[:new] += 1
361
409
  puts "NEW: #{filename}" unless @options[:quiet]
410
+ new_files_insert filename, digest
362
411
  unless @options[:test_only]
363
- @new_files[filename] = digest
364
- insert filename, mtime, digest
412
+ digests_insert filename, mtime, digest
365
413
  end
366
414
  end
367
415
 
@@ -369,29 +417,31 @@ class FileDigests
369
417
  # Renames and missing files
370
418
 
371
419
  def track_renames
372
- @missing_files.delete_if do |filename, digest|
373
- if @new_files.value?(digest)
374
- @counters[:renamed] += 1
375
- unless @options[:test_only]
376
- delete_by_filename filename
377
- end
378
- true
379
- end
420
+ unless @options[:test_only]
421
+ digests_delete_renamed_files
380
422
  end
423
+ missing_files_delete_renamed_files
424
+ @counters[:renamed] = @db.changes
381
425
  end
382
426
 
383
427
  def print_missing_files
384
428
  puts "\nMISSING FILES:"
385
- @missing_files.sort.to_h.each do |filename, digest|
386
- puts filename
429
+ missing_files_select_all_filenames.each do |record|
430
+ puts record["filename"]
387
431
  end
388
432
  end
389
433
 
390
434
  def remove_missing_files
391
- @missing_files.each do |filename, digest|
392
- delete_by_filename filename
393
- end
394
- @missing_files = {}
435
+ digests_delete_all_missing_files
436
+ missing_files_delete_all
437
+ end
438
+
439
+ def missing_files_count
440
+ missing_files_count_query!&.first&.first
441
+ end
442
+
443
+ def any_missing_files?
444
+ missing_files_count > 0
395
445
  end
396
446
 
397
447
 
@@ -402,6 +452,7 @@ class FileDigests
402
452
  end
403
453
 
404
454
  def integrity_check
455
+ puts "Checking database integrity..." if @options[:verbose]
405
456
  if execute("PRAGMA integrity_check")&.first&.fetch("integrity_check") != "ok"
406
457
  raise "Database integrity check failed"
407
458
  end
@@ -456,7 +507,7 @@ class FileDigests
456
507
  end
457
508
 
458
509
  def find_by_filename filename
459
- result = find_by_filename_query filename
510
+ result = digests_find_by_filename_query filename
460
511
  found = result.next
461
512
  raise "Multiple records found" if result.next
462
513
  found
@@ -476,6 +527,18 @@ class FileDigests
476
527
  end
477
528
  end
478
529
 
530
+ def check_if_database_is_at_certain_version target_version
531
+ current_version = get_metadata("database_version")
532
+ if current_version != target_version
533
+ STDERR.puts "This version of file-digests (#{FileDigests::VERSION || "unknown"}) is only compartible with the database version #{target_version}. Current database version is #{current_version}. To use this database, please install appropriate version if file-digest."
534
+ raise "Incompatible database version"
535
+ end
536
+ end
537
+
538
+ def new_files_count
539
+ new_files_count_query!&.first&.first
540
+ end
541
+
479
542
 
480
543
  # Filesystem-related helpers
481
544
 
@@ -514,18 +577,13 @@ class FileDigests
514
577
  digest.update(buffer)
515
578
  new_digest.update(buffer) if @new_digest_algorithm
516
579
  end
517
- @new_digests[digest.hexdigest] = new_digest.hexdigest if @new_digest_algorithm
518
- return digest.hexdigest
580
+ return [digest.hexdigest, (new_digest.hexdigest if @new_digest_algorithm)]
519
581
  end
520
582
  end
521
583
 
522
584
 
523
585
  # Runtime state helpers
524
586
 
525
- def any_missing_files?
526
- @missing_files.length > 0
527
- end
528
-
529
587
  def any_exceptions?
530
588
  @counters[:exceptions] > 0
531
589
  end
@@ -539,14 +597,17 @@ class FileDigests
539
597
  def confirm text
540
598
  if STDIN.tty? && STDOUT.tty?
541
599
  puts "#{text} (y/n)?"
542
- STDIN.gets.strip.downcase == "y"
600
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
601
+ result = (STDIN.gets.strip.downcase == "y")
602
+ @user_input_wait_time += (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
603
+ result
543
604
  end
544
605
  end
545
606
 
546
607
  def measure_time
547
608
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
548
609
  yield
549
- elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
610
+ elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) - @user_input_wait_time
550
611
  puts "Elapsed time: #{elapsed.to_i / 3600}h #{(elapsed.to_i % 3600) / 60}m #{"%.3f" % (elapsed % 60)}s" unless @options[:quiet]
551
612
  end
552
613
 
@@ -564,11 +625,14 @@ class FileDigests
564
625
  end
565
626
 
566
627
  def print_counters
628
+ missing_files_count_result = missing_files_count
629
+ new_files_count_result = new_files_count - @counters[:renamed]
630
+
567
631
  puts "#{@counters[:good]} file(s) passes digest check" if @counters[:good] > 0
568
632
  puts "#{@counters[:updated]} file(s) are updated" if @counters[:updated] > 0
569
- puts "#{@counters[:new]} file(s) are new" if @counters[:new] > 0
633
+ puts "#{new_files_count_result} file(s) are new" if new_files_count_result > 0
570
634
  puts "#{@counters[:renamed]} file(s) are renamed" if @counters[:renamed] > 0
571
- puts "#{@missing_files.length} file(s) are missing" if @missing_files.length > 0
635
+ puts "#{missing_files_count_result} file(s) are missing" if missing_files_count_result > 0
572
636
  puts "#{@counters[:likely_damaged]} file(s) are likely damaged (!)" if @counters[:likely_damaged] > 0
573
637
  puts "#{@counters[:exceptions]} file(s) had exceptions occured during processing (!)" if @counters[:exceptions] > 0
574
638
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: file-digests
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.32
4
+ version: 0.0.37
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stanislav Senotrusov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-15 00:00:00.000000000 Z
11
+ date: 2020-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: openssl