safeguard 0.0.3 → 0.1.0

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.
data/README.markdown CHANGED
@@ -1,3 +1,13 @@
1
1
  # Safeguard
2
2
 
3
- ## Hash-based file integrity verification utility
3
+ Hash-based file integrity verification utility
4
+
5
+ # Installation
6
+
7
+ Latest version:
8
+
9
+ gem install safeguard
10
+
11
+ From source:
12
+
13
+ git clone git://github.com/matheusmoreira/safeguard.git
data/bin/safeguard CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'safeguard'
3
3
 
4
- Safeguard::Command.run *ARGV
4
+ Safeguard.run *ARGV
data/i18n/en.yml ADDED
@@ -0,0 +1,11 @@
1
+ en:
2
+ safeguard:
3
+ output:
4
+ terminal:
5
+ before_verifying: "Verifying '%{file}' with %{function}..."
6
+ before_hashing: "Hashing '%{file}' with %{function}..."
7
+ after_hashing: ' %{hash}'
8
+ after_verifying: ' %{result}'
9
+ hash_missing: 'Hash not in repository'
10
+ file_missing: 'File not in repository'
11
+ initialized_repository: 'Initialized Safeguard repository in %{directory}'
data/i18n/pt.yml ADDED
@@ -0,0 +1,11 @@
1
+ pt:
2
+ safeguard:
3
+ output:
4
+ terminal:
5
+ before_verifying: "Verificando o %{function} de '%{file}'..."
6
+ before_hashing: "Calculando o %{function} de '%{file}'..."
7
+ after_hashing: ' %{hash}'
8
+ after_verifying: ' %{result}'
9
+ hash_missing: 'Hash não está presente no repositório'
10
+ file_missing: 'Arquivo não está presente no repositório'
11
+ initialized_repository: 'Repositório do Safeguard inicializado em %{directory}'
@@ -0,0 +1,31 @@
1
+ require 'safeguard/command/add'
2
+ require 'safeguard/output/terminal'
3
+ require 'safeguard/repository'
4
+ require 'ribbon'
5
+
6
+ module Safeguard
7
+ class Command
8
+ class Add
9
+
10
+ # Hashes files and stores the result in the repository.
11
+ #
12
+ # $ safeguard add hash --sha1 *.mp3
13
+ class Hash < Add
14
+
15
+ add_supported_algorithms_as_options!
16
+ opt :force, 'Rehash files that are already in the repository'
17
+
18
+ when_called do |options, files|
19
+ Repository.new(options.dir).before_save do |repo|
20
+ functions = options.functions? []
21
+ hasher_options = Ribbon.new force: options.force?, functions: functions
22
+ mappings = Output::Terminal.create_mappings_for :before_hashing, :after_hashing
23
+ Ribbon.merge! hasher_options, mappings
24
+ repo.hash_and_add! *files, hasher_options
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,26 +1,19 @@
1
1
  require 'safeguard/command'
2
- require 'safeguard/digest'
2
+ require 'safeguard/repository'
3
3
 
4
4
  module Safeguard
5
5
  class Command
6
6
 
7
- # Adds files to a Repository.
7
+ # Adds files to a Repository without hashing them.
8
+ #
9
+ # $ safeguard add *.mp3
8
10
  class Add < Command
9
11
 
10
- opt :functions, '--use', Symbol,
11
- "Algorithm used to calculate the file's checksum. " <<
12
- "Currently supported: #{Digest::SUPPORTED_ALGORITHMS.join(', ')}",
13
- arity: [1,0], on_multiple: :append
14
-
15
- opt :force, '--force', 'Rehash files that are already in the repository'
16
-
17
- # For every argument, try to add it to the Repository in the current
18
- # directory.
19
- action do |options, args|
12
+ # For every argument, add it to the Repository in the current directory.
13
+ action do |options, files|
20
14
  repo = Repository.new options.dir
21
15
  repo.before_save do
22
- repo.add_files! *args, :functions => options.functions,
23
- :force => options.force?
16
+ repo.add! *files
24
17
  end
25
18
  end
26
19
 
@@ -1,33 +1,24 @@
1
1
  require 'safeguard/command'
2
- require 'safeguard/digest'
2
+ require 'safeguard/output/terminal'
3
+ require 'safeguard/repository'
4
+ require 'ribbon'
3
5
 
4
6
  module Safeguard
5
7
  class Command
6
8
 
7
9
  # Outputs the checksum of files.
8
10
  #
9
- # $ safeguard hash *.mp3
10
- #
11
- # The hash function can be specified with the --hash-function option. The
12
- # default is SHA1, but MD5 and CRC32 are also supported.
13
- #
14
- # $ safeguard --hash-function md5 hash *.mp3
11
+ # $ safeguard hash --sha1 --md5 --crc32 *.mp3
15
12
  class Hash < Command
16
13
 
17
- opt :func, '--hash-function', '--hash-functions', Symbol,
18
- "Algorithm to use to calculate the file's checksum. " <<
19
- "Currently supported: #{Digest::SUPPORTED_ALGORITHMS.join(', ')}",
20
- default: :sha1, arity: [1,-1]
14
+ add_supported_algorithms_as_options!
21
15
 
22
16
  # For every argument, outputs its checksum if it exists as a file.
23
- action do |options, args|
24
- hasher = Hasher.new *args, :functions => options.func
25
- hasher.each do |file, hash_data|
26
- puts "#{file}:"
27
- hasher.functions.each do |function|
28
- puts "\t#{function} => #{hash_data[function]}"
29
- end
30
- end
17
+ action do |options, files|
18
+ hasher_options = Ribbon.new functions: options.functions?([])
19
+ mappings = Output::Terminal.create_mappings_for :before_hashing, :after_hashing
20
+ Ribbon.merge! hasher_options, mappings
21
+ Hasher.new(*files, hasher_options).hash_files!
31
22
  end
32
23
 
33
24
  end
@@ -1,13 +1,13 @@
1
1
  require 'safeguard/command'
2
+ require 'safeguard/output/terminal'
2
3
  require 'safeguard/repository'
3
- require 'fileutils'
4
4
 
5
5
  module Safeguard
6
6
  class Command
7
7
 
8
8
  # Initializes a Repository in a given directory.
9
9
  #
10
- # $ safeguard init .
10
+ # $ safeguard init
11
11
  class Init < Command
12
12
 
13
13
  # Initializes a Safeguard Repository in a directory, which is either the
@@ -17,7 +17,7 @@ module Safeguard
17
17
  dir = File.expand_path(args.pop || options.dir)
18
18
  repo = Repository.new(dir)
19
19
  repo.create_directory!
20
- puts "Initialized safeguard repository in #{repo.dir}"
20
+ Output::Terminal.initialized_repository repo.dir
21
21
  end
22
22
 
23
23
  end
@@ -1,31 +1,31 @@
1
1
  require 'safeguard/command'
2
+ require 'safeguard/output/terminal'
3
+ require 'safeguard/repository'
4
+ require 'ribbon'
2
5
 
3
6
  module Safeguard
4
7
  class Command
5
8
 
6
9
  # Verifies the files present in a Repository.
10
+ #
11
+ # # Verifies all files in repository using SHA1
12
+ # $ safeguard verify --sha1
13
+ #
14
+ # # Verifies all files ending in '.mp3' using CRC32
15
+ # $ safeguard verify --crc32 *.mp3
7
16
  class Verify < Command
8
17
 
9
- opt :functions, '--use', Symbol,
10
- "Algorithm used to calculate the file's checksum. " <<
11
- "Currently supported: #{Digest::SUPPORTED_ALGORITHMS.join(', ')}",
12
- arity: [1,0], on_multiple: :append
18
+ add_supported_algorithms_as_options!
13
19
 
14
20
  # Verify the files passed as arguments using information from the
15
21
  # Repository in the current directory.
16
- action do |options, args|
22
+ action do |options, files|
17
23
  repo = Repository.new options.dir
18
- functions = options.functions
19
- results = repo.verify_files *args, functions: functions
20
- results.keys.each do |file|
21
- puts "#{file}:"
22
- results[file].keys.each do |function|
23
- value = results[file][function]
24
- value = "File not in repository" if value == :file_missing
25
- value = "Hash not in repository" if value == :hash_missing
26
- puts "\t#{function} => #{value}"
27
- end
28
- end
24
+ functions = options.functions? []
25
+ verifier_options = Ribbon.new functions: functions
26
+ mappings = Output::Terminal.create_mappings_for :before_verifying, :after_verifying
27
+ Ribbon.merge! verifier_options, mappings
28
+ results = repo.verify_files *files, verifier_options
29
29
  end
30
30
 
31
31
  end
@@ -1,5 +1,7 @@
1
+ require 'safeguard/digest'
2
+ require 'safeguard/version'
1
3
  require 'acclaim/command'
2
- require 'acclaim/version'
4
+ require 'i18n'
3
5
 
4
6
  module Safeguard
5
7
 
@@ -9,20 +11,50 @@ module Safeguard
9
11
  class Command < Acclaim::Command
10
12
 
11
13
  help
12
- version Acclaim::Version::STRING
14
+ version Safeguard::Version::STRING
13
15
 
14
- opt :dir, '-D', '--dir', '--directory', 'Directory in which the repository is.',
16
+ opt :dir, '-D', '--directory', 'Directory in which the repository is.',
15
17
  default: Dir.pwd, arity: [1,0]
16
18
 
19
+ opt :locale, Symbol, 'The locale to use for language & localization.',
20
+ arity: [1,0] do |options, locale|
21
+ I18n.locale = locale.shift
22
+ end
23
+
17
24
  action do |options, args|
18
25
  Init.execute options, args
19
26
  end
20
27
 
28
+ # The class methods.
29
+ class << self
30
+
31
+ # An option will be made for every element in
32
+ # Digest::SUPPORTED_ALGORITHMS. They may be used together and always
33
+ # append to the function array, bound to the <tt>:functions</tt> key.
34
+ #
35
+ # However, due to the lack of a formally defined +functions+ option,
36
+ # the Acclaim option parser will not set that option to anything.
37
+ # Therefore, calling <tt>options.functions</tt> will return an empty
38
+ # ribbon if the user doesn't specify any functions on the command line.
39
+ #
40
+ # <tt>options.functions?</tt> should ALWAYS be called, preferably while
41
+ # providing an empty array as fallback.
42
+ def add_supported_algorithms_as_options!
43
+ Digest::SUPPORTED_ALGORITHMS.each do |algorithm|
44
+ option algorithm, "Use #{algorithm.to_s.upcase}." do |options|
45
+ options.functions = (options.functions? []) << algorithm
46
+ end
47
+ end
48
+ end
49
+
50
+ end
51
+
21
52
  end
22
53
 
23
54
  end
24
55
 
25
56
  require 'safeguard/command/add'
57
+ require 'safeguard/command/add/hash'
26
58
  require 'safeguard/command/hash'
27
59
  require 'safeguard/command/init'
28
60
  require 'safeguard/command/verify'
@@ -1,5 +1,7 @@
1
1
  module Safeguard
2
2
  module CoreExt
3
+
4
+ # Ruby core extensions for the File class.
3
5
  module File
4
6
 
5
7
  # Default size of chunks in bytes. A megabyte is equal to 2^20 bytes, or
@@ -0,0 +1,16 @@
1
+ module Safeguard
2
+ module CoreExt
3
+
4
+ # Ruby core extensions for the Module class.
5
+ module Module
6
+
7
+ def translation_key(*args)
8
+ name.split('::').push(*args).map(&:to_s).map(&:downcase).join '.'
9
+ end
10
+
11
+ end
12
+
13
+ ::Module.send :include, Safeguard::CoreExt::Module
14
+
15
+ end
16
+ end
@@ -7,56 +7,54 @@ module Safeguard
7
7
  # Encapsulates checksum computation for files using a set of hash functions.
8
8
  module Digest
9
9
 
10
- SUPPORTED_ALGORITHMS = %w(sha1 md5 crc32).map!(&:to_sym).freeze
11
-
12
- # Digests a file using an +algorithm+.
13
- #
14
- # Algorithms are expected include the Digest::Instance module.
15
- #
16
- # OpenSSL::Digest::SHA1.include? Digest::Instance
17
- # => true
18
- def self.digest_with(algorithm, filename)
19
- digest = algorithm.new
20
- File.open(filename, 'rb') do |file|
21
- file.each_chunk do |chunk|
22
- digest << chunk
23
- end
24
- end
25
- digest.hexdigest!
26
- end
10
+ algorithms = { sha1: OpenSSL::Digest::SHA1,
11
+ md5: OpenSSL::Digest::MD5,
12
+ crc32: Safeguard::Digest::CRC32 }.freeze
27
13
 
28
- private_class_method :digest_with
14
+ SUPPORTED_ALGORITHMS = algorithms.keys.freeze
29
15
 
30
- # Computes the SHA1 sum of the given file.
31
- def self.sha1(*args)
32
- digest_with OpenSSL::Digest::SHA1, *args
16
+ # Define one method for every supported algorithm.
17
+ algorithms.each do |algorithm, digest_class|
18
+ define_singleton_method algorithm do |*args|
19
+ digest_with digest_class, *args
20
+ end
33
21
  end
34
22
 
35
- # Computes the MD5 sum of the given file.
36
- def self.md5(*args)
37
- digest_with OpenSSL::Digest::MD5, *args
38
- end
23
+ # The class methods.
24
+ class << self
25
+
26
+ # Digests a file using an +algorithm+.
27
+ #
28
+ # Algorithms are expected include the Digest::Instance module.
29
+ #
30
+ # OpenSSL::Digest::SHA1.include? Digest::Instance
31
+ # => true
32
+ def digest_with(algorithm, filename)
33
+ digest = algorithm.new
34
+ File.open(filename, 'rb') do |file|
35
+ file.each_chunk do |chunk|
36
+ digest << chunk
37
+ end
38
+ end
39
+ digest.hexdigest!
40
+ end
39
41
 
40
- # Computes the CRC32 sum of the given file.
41
- def self.crc32(*args)
42
- # Read file in binary mode. Doesn't make any difference in *nix, but Ruby
43
- # will attempt to convert line endings if the file is opened in text mode
44
- # in other platforms.
45
- digest_with Safeguard::Digest::CRC32, *args
46
- end
42
+ # Digests a file using a hash function, which can be the symbol of any
43
+ # Digest module method that takes a file. Uses SHA1 by default.
44
+ #
45
+ # Safeguard::Digest.file(file, :md5)
46
+ #
47
+ # Is equivalent to:
48
+ #
49
+ # Safeguard::Digest.md5(file)
50
+ def file(file, hash_function = :sha1)
51
+ hash_function = hash_function.to_sym
52
+ unless respond_to? hash_function
53
+ raise ArgumentError, "Unsupported hash function: #{hash_function}"
54
+ end
55
+ send hash_function, file
56
+ end
47
57
 
48
- # Digests a file using a hash function, which can be the symbol of any
49
- # Digest module method that takes a file. Uses SHA1 by default.
50
- #
51
- # Safeguard::Digest.file(file, :md5)
52
- #
53
- # Is equivalent to:
54
- #
55
- # Safeguard::Digest.md5(file)
56
- def self.file(file, hash_function = :sha1)
57
- f = hash_function.to_sym
58
- raise ArgumentError, "Unsupported hash function: #{f}" unless respond_to? f
59
- send hash_function, file
60
58
  end
61
59
 
62
60
  end
@@ -1,11 +1,12 @@
1
1
  require 'safeguard/digest'
2
+ require 'safeguard/worker'
2
3
  require 'ribbon'
3
4
  require 'ribbon/core_ext/array'
4
5
 
5
6
  module Safeguard
6
7
 
7
8
  # Hashes a set of files.
8
- class Hasher
9
+ class Hasher < Worker
9
10
 
10
11
  # The files which will be hashed.
11
12
  attr_accessor :files
@@ -13,6 +14,9 @@ module Safeguard
13
14
  # The hash functions to use.
14
15
  attr_accessor :functions
15
16
 
17
+ # Available callbacks.
18
+ has_callbacks :before_hashing, :after_hashing
19
+
16
20
  # Initializes a new hasher with the given files. Last argument can be an
17
21
  # options hash or ribbon which specifies the hash functions to use:
18
22
  #
@@ -20,12 +24,13 @@ module Safeguard
20
24
  # Hasher.new *files, :functions => :sha1
21
25
  # Hasher.new *files, :functions => [ :sha1, :md5, :crc32 ]
22
26
  def initialize(*args)
23
- ribbon = args.extract_options_as_ribbon!
24
- funcs = ribbon.functions? do
27
+ options = args.extract_ribbon!
28
+ funcs = options.functions? do
25
29
  raise ArgumentError, 'No hash functions specified'
26
30
  end
27
31
  self.functions = [*funcs]
28
32
  self.files = args
33
+ initialize_callbacks_from options
29
34
  end
30
35
 
31
36
  # Calculates the hash of each file. Updates the cached hash results.
@@ -33,7 +38,9 @@ module Safeguard
33
38
  results = Ribbon.new
34
39
  files.each do |file|
35
40
  functions.each do |function|
36
- results[file][function] = Safeguard::Digest.file file, function
41
+ call_callback before_hashing, file, function
42
+ results[file][function] = result = Safeguard::Digest.file file, function
43
+ call_callback after_hashing, file, function, result
37
44
  end
38
45
  end
39
46
  results = Ribbon[results]
@@ -0,0 +1,74 @@
1
+ require 'safeguard/core_ext/module'
2
+ require 'i18n'
3
+
4
+ module Safeguard
5
+ module Output
6
+
7
+ # Encapsulates all terminal output.
8
+ module Terminal
9
+
10
+ class << self
11
+
12
+ # Appends the given +key+ to this module's translation key and forwards
13
+ # all other arguments to <tt>I18n.translate</tt>.
14
+ def translate(key, *args)
15
+ I18n.t translation_key(key), *args
16
+ end
17
+
18
+ # Prints a string before a file is verified without a new line.
19
+ def before_verifying(file, function)
20
+ options = { file: file, function: function }
21
+ print translate(:before_verifying, preprocess(options))
22
+ end
23
+
24
+ # Prints a string before a file is hashed without a new line.
25
+ def before_hashing(file, function)
26
+ options = { file: file, function: function }
27
+ print translate(:before_hashing, preprocess(options))
28
+ end
29
+
30
+ # Prints a string after a file is hashed with a new line.
31
+ def after_hashing(file, function, hash)
32
+ options = { file: file, function: function, hash: hash }
33
+ puts translate(:after_hashing, preprocess(options))
34
+ end
35
+
36
+ # Prints a string after a file is verified with a new line.
37
+ def after_verifying(file, function, result)
38
+ options = { file: file, function: function, result: result }
39
+ puts translate(:after_verifying, preprocess(options))
40
+ end
41
+
42
+ # Creates an options ribbon containing containing mappings to this
43
+ # class' methods.
44
+ def create_mappings_for(*keys)
45
+ options = Ribbon.new
46
+ keys.each { |key| options[key] = method key }
47
+ options
48
+ end
49
+
50
+ def initialized_repository(directory)
51
+ options = { directory: directory }
52
+ puts translate(:initialized_repository, preprocess(options))
53
+ end
54
+
55
+ private
56
+
57
+ # Applies common preprocessing.
58
+ def preprocess(options = {})
59
+ options[:function] &&= options[:function].to_s.upcase
60
+ options[:result] &&= case result = options[:result]
61
+ when false, nil then :Mismatch
62
+ when true then :OK
63
+ else translate result
64
+ end
65
+ options
66
+ end
67
+
68
+ end
69
+
70
+
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,7 @@
1
+ module Safeguard
2
+
3
+ # Encapsulates all output.
4
+ module Output
5
+ end
6
+
7
+ end
@@ -11,6 +11,7 @@ module Safeguard
11
11
 
12
12
  # Initializes this hash table with the contents of the given Ribbon.
13
13
  def initialize(ribbon = nil, &block)
14
+ ribbon = ribbon.ribbon if self.class === ribbon
14
15
  merge! ribbon, &block if ribbon
15
16
  end
16
17
 
@@ -27,14 +28,47 @@ module Safeguard
27
28
  end
28
29
 
29
30
  # Merges this hash table's data with the other's.
30
- def merge!(other)
31
- other = other.ribbon if other.is_a? HashTable
32
- Ribbon.deep_merge! ribbon, other
31
+ def merge!(other, &block)
32
+ ribbon.deep_merge! other, &block
33
+ end
34
+
35
+ # Fetches a value. Same as Hash#fetch.
36
+ def fetch(*args, &block)
37
+ ribbon.fetch *args, &block
33
38
  end
34
39
 
35
40
  # Looks up the checksum data for the given +filename+.
36
41
  def [](filename)
37
- ribbon[filename]
42
+ ribbon.ribbon[filename]
43
+ end
44
+
45
+ # Adds a file to the hash table.
46
+ #
47
+ # The file will not have any hash information associated with it.
48
+ def add(*filenames)
49
+ filenames.each { |filename| self[filename] }
50
+ end
51
+
52
+ # Same as #add, but returns +self+.
53
+ def <<(filename)
54
+ add filename
55
+ self
56
+ end
57
+
58
+ # Returns whether or not the given file is present in this hash table.
59
+ def has_file?(filename)
60
+ ribbon.has_key? filename
61
+ end
62
+
63
+ # Returns whether the given file has any hash information associated.
64
+ def has_hashes?(filename, *functions)
65
+ has_file?(filename) and not self[filename].empty?
66
+ end
67
+
68
+ # Returns whether the given file has a hash associated with the function
69
+ # stored.
70
+ def has_hash?(filename, function)
71
+ has_file?(filename) and self[filename].fetch function, nil
38
72
  end
39
73
 
40
74
  # Returns a list of files present in this hash table.
@@ -45,7 +79,12 @@ module Safeguard
45
79
  # The underlying wrapped ribbon used to store filenames and their
46
80
  # associated hashes.
47
81
  def ribbon
48
- @ribbon ||= Ribbon::Wrapper.new
82
+ (@ribbon ||= Ribbon::Wrapper.new).tap do |ribbon|
83
+ # TODO: Possible bottleneck here. #wrap_all! is a recursive call.
84
+ # Keep its usage localized instead of calling it on every access?
85
+ # Once after every modification, perhaps?
86
+ ribbon.wrap_all!
87
+ end
49
88
  end
50
89
 
51
90
  end
@@ -53,14 +53,22 @@ module Safeguard
53
53
  save_hash_table
54
54
  end
55
55
 
56
+ # Adds the given files to the repository.
57
+ def add!(*files)
58
+ hash_table.add *files
59
+ end
60
+
56
61
  # Calculates the checksum of the given files and stores the results. +args+
57
62
  # will be used to instantiate a new Hasher.
58
- def add_files!(*args)
59
- ribbon = args.extract_ribbon!
60
- hasher = Hasher.new *args, ribbon
63
+ #
64
+ # By default, the hashes of files already in the repository will not be
65
+ # recalculated. To force that, call with <tt>:force => true</tt>.
66
+ def hash_and_add!(*args)
67
+ options = args.extract_ribbon!
68
+ hasher = Hasher.new *args, options
61
69
  hasher.files.delete_if do |file|
62
- hash_table.files.include? file
63
- end unless ribbon.force?
70
+ hasher.functions.any? { |function| hash_table.has_hash? file, function }
71
+ end unless options.force?
64
72
  results = hasher.results
65
73
  hash_table.merge! results
66
74
  end
@@ -71,7 +79,7 @@ module Safeguard
71
79
  end
72
80
 
73
81
  # Creates a verifier using this repository's hash table.
74
- def create_verifier_with(*args)
82
+ def create_verifier_for(*args)
75
83
  ribbon = args.extract_wrapped_ribbon!
76
84
  Verifier.new *args, ribbon.merge!(hash_table: hash_table)
77
85
  end
@@ -79,7 +87,7 @@ module Safeguard
79
87
  # Recalculates the checksum of the given files and compares them to the
80
88
  # stored values. +args+ will be used to instantiate a new Verifier.
81
89
  def verify_files(*args)
82
- create_verifier_with(*args).results
90
+ create_verifier_for(*args).results
83
91
  end
84
92
 
85
93
  # Returns the path to the repository relative to the given +dir+.
@@ -1,12 +1,13 @@
1
1
  require 'safeguard/hasher'
2
2
  require 'safeguard/repository/hash_table'
3
+ require 'safeguard/worker'
3
4
  require 'ribbon'
4
5
  require 'ribbon/core_ext/array'
5
6
 
6
7
  module Safeguard
7
8
 
8
9
  # Rehashes a set of files and compares the results to the stored hashes.
9
- class Verifier
10
+ class Verifier < Worker
10
11
 
11
12
  # The hasher used to calculate the checksum of the files.
12
13
  attr_accessor :hasher
@@ -14,6 +15,9 @@ module Safeguard
14
15
  # Hash table to compare results against.
15
16
  attr_accessor :hash_table
16
17
 
18
+ # Available callbacks.
19
+ has_callbacks :before_verifying, :after_verifying
20
+
17
21
  # Initializes a new verifier with the given files. Last argument can be an
18
22
  # options hash or ribbon which specifies the hash table to verify against
19
23
  # and the Hasher to use. If a hasher isn't specified, a new one will be
@@ -28,17 +32,16 @@ module Safeguard
28
32
  # Verifier.new :functions => [ :crc32, :md5 ], :hash_table => table
29
33
  # Verifier.new *files, :functions => :crc32, :hash_table => table
30
34
  def initialize(*args)
31
- ribbon = args.extract_ribbon!
32
- table = ribbon.hash_table? do
35
+ options = args.extract_ribbon!
36
+ table = options.hash_table? do
33
37
  raise ArgumentError, 'No hash table to verify against'
34
38
  end
35
- table = table.ribbon if Repository::HashTable === table
36
- self.hash_table = Ribbon[table]
37
- hash_table.wrap_all!
38
- args = hash_table.keys if args.empty?
39
- self.hasher = ribbon.hasher? do
40
- Hasher.new *args, ribbon
39
+ self.hash_table = Repository::HashTable.new table
40
+ args = hash_table.files if args.empty?
41
+ self.hasher = options.hasher? do
42
+ Hasher.new *args, options
41
43
  end
44
+ initialize_callbacks_from options
42
45
  end
43
46
 
44
47
  # Uses the hasher to recalculate the hashes of the files using the specified
@@ -52,8 +55,9 @@ module Safeguard
52
55
  results = Ribbon.new
53
56
  hasher.each do |file, hash_data|
54
57
  hasher.functions.each do |function|
55
- results[file][function] = if hash_table.has_key? file
56
- if hash_table[file].has_key? function
58
+ call_callback before_verifying, file, function
59
+ results[file][function] = result = if hash_table.has_file? file
60
+ if hash_table.has_hash? file, function
57
61
  hash_data[function] == hash_table[file][function]
58
62
  else
59
63
  :hash_missing
@@ -61,6 +65,7 @@ module Safeguard
61
65
  else
62
66
  :file_missing
63
67
  end
68
+ call_callback after_verifying, file, function, result
64
69
  end
65
70
  end
66
71
  results = Ribbon[results]
@@ -11,12 +11,12 @@ module Safeguard
11
11
  # Minor version.
12
12
  #
13
13
  # Increments denote backward-compatible changes and additions.
14
- MINOR = 0
14
+ MINOR = 1
15
15
 
16
16
  # Patch version.
17
17
  #
18
18
  # Increments denote changes in implementation.
19
- PATCH = 3
19
+ PATCH = 0
20
20
 
21
21
  # Build version.
22
22
  #
@@ -0,0 +1,56 @@
1
+ require 'ribbon'
2
+
3
+ module Safeguard
4
+
5
+ # Something that works behind the scenes, possibly talking to an user
6
+ # interface in the middle of it through callbacks.
7
+ class Worker
8
+
9
+ class << self
10
+
11
+ # All callbacks defined for this class.
12
+ def callbacks
13
+ @callbacks ||= []
14
+ end
15
+
16
+ # Defines callbacks for this class.
17
+ def has_callback(*names)
18
+ callbacks.push *names
19
+ names.map(&:to_sym).each do |callback|
20
+ iv_name = callback.to_s.prepend('@').to_sym
21
+ define_method callback do |&block|
22
+ assign_callback iv_name, &block
23
+ end
24
+ end
25
+ end
26
+
27
+ # Same as +has_callback+.
28
+ alias has_callbacks has_callback
29
+
30
+ end
31
+
32
+ protected
33
+
34
+ # Calls the given callback, passing it the rest of the arguments.
35
+ def call_callback(block, *args)
36
+ block.call *args if block.respond_to? :call
37
+ end
38
+
39
+ def initialize_callbacks_from(options)
40
+ options = Ribbon.wrap options
41
+ self.class.callbacks.each do |callback|
42
+ send callback, &options.fetch(callback, nil)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ # Sets the callback to the given variable name if the block responds to
49
+ # +call+.
50
+ def assign_callback(name, &block)
51
+ instance_variable_set name, block if block.respond_to? :call
52
+ instance_variable_get name
53
+ end
54
+
55
+ end
56
+ end
data/lib/safeguard.rb CHANGED
@@ -1,16 +1,49 @@
1
- require 'safeguard/command'
2
- require 'safeguard/digest'
3
- require 'safeguard/hasher'
4
- require 'safeguard/repository'
5
- require 'safeguard/verifier'
6
- require 'safeguard/version'
1
+ require 'i18n'
7
2
 
8
3
  # Safeguard module.
9
4
  module Safeguard
10
5
 
11
- # Run a command by name with the given arguments.
12
- def self.run(command, *args)
13
- Command.invoke(command, *args)
6
+ class << self
7
+
8
+ # Run a command by name with the given arguments.
9
+ def run(*args)
10
+ initialize_i18n
11
+ Command.run *args
12
+ end
13
+
14
+ # Returns the version of Safeguard.
15
+ def version
16
+ Version::STRING
17
+ end
18
+
19
+ # Where the Safeguard installation is located.
20
+ def root
21
+ File.expand_path '../..', __FILE__
22
+ end
23
+
24
+ # Directory where translations are kept.
25
+ def i18n
26
+ File.join root, 'i18n'
27
+ end
28
+
29
+ # Array of translation files.
30
+ def translation_files
31
+ Dir[File.join(i18n, '*')]
32
+ end
33
+
34
+ def initialize_i18n
35
+ I18n.load_path = translation_files
36
+ end
37
+
14
38
  end
15
39
 
16
40
  end
41
+
42
+ require 'safeguard/command'
43
+ require 'safeguard/digest'
44
+ require 'safeguard/hasher'
45
+ require 'safeguard/output'
46
+ require 'safeguard/repository'
47
+ require 'safeguard/verifier'
48
+ require 'safeguard/version'
49
+ require 'safeguard/worker'
data/safeguard.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new('safeguard') do |gem|
19
19
 
20
20
  gem.add_runtime_dependency 'acclaim'
21
21
  gem.add_runtime_dependency 'ribbon'
22
+ gem.add_runtime_dependency 'i18n'
22
23
 
23
24
  gem.add_development_dependency 'rookie'
24
25
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safeguard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-12 00:00:00.000000000 Z
12
+ date: 2012-01-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: acclaim
16
- requirement: &6175680 !ruby/object:Gem::Requirement
16
+ requirement: &18215560 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *6175680
24
+ version_requirements: *18215560
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: ribbon
27
- requirement: &6174640 !ruby/object:Gem::Requirement
27
+ requirement: &18215080 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,21 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *6174640
35
+ version_requirements: *18215080
36
+ - !ruby/object:Gem::Dependency
37
+ name: i18n
38
+ requirement: &18214540 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *18214540
36
47
  - !ruby/object:Gem::Dependency
37
48
  name: rookie
38
- requirement: &6174080 !ruby/object:Gem::Requirement
49
+ requirement: &18214080 !ruby/object:Gem::Requirement
39
50
  none: false
40
51
  requirements:
41
52
  - - ! '>='
@@ -43,7 +54,7 @@ dependencies:
43
54
  version: '0'
44
55
  type: :development
45
56
  prerelease: false
46
- version_requirements: *6174080
57
+ version_requirements: *18214080
47
58
  description: Hash-based file integrity verification utility
48
59
  email: matheus.a.m.moreira@gmail.com
49
60
  executables:
@@ -58,20 +69,27 @@ files:
58
69
  - README.markdown
59
70
  - Rakefile
60
71
  - bin/safeguard
72
+ - i18n/en.yml
73
+ - i18n/pt.yml
61
74
  - lib/safeguard.rb
62
75
  - lib/safeguard/command.rb
63
76
  - lib/safeguard/command/add.rb
77
+ - lib/safeguard/command/add/hash.rb
64
78
  - lib/safeguard/command/hash.rb
65
79
  - lib/safeguard/command/init.rb
66
80
  - lib/safeguard/command/verify.rb
67
81
  - lib/safeguard/core_ext/file.rb
82
+ - lib/safeguard/core_ext/module.rb
68
83
  - lib/safeguard/digest.rb
69
84
  - lib/safeguard/digest/crc32.rb
70
85
  - lib/safeguard/hasher.rb
86
+ - lib/safeguard/output.rb
87
+ - lib/safeguard/output/terminal.rb
71
88
  - lib/safeguard/repository.rb
72
89
  - lib/safeguard/repository/hash_table.rb
73
90
  - lib/safeguard/verifier.rb
74
91
  - lib/safeguard/version.rb
92
+ - lib/safeguard/worker.rb
75
93
  - safeguard.gemspec
76
94
  homepage: https://github.com/matheusmoreira/safeguard
77
95
  licenses: []
@@ -87,7 +105,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
87
105
  version: '0'
88
106
  segments:
89
107
  - 0
90
- hash: -397877248667330061
108
+ hash: -537913789085051653
91
109
  required_rubygems_version: !ruby/object:Gem::Requirement
92
110
  none: false
93
111
  requirements:
@@ -96,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
114
  version: '0'
97
115
  segments:
98
116
  - 0
99
- hash: -397877248667330061
117
+ hash: -537913789085051653
100
118
  requirements: []
101
119
  rubyforge_project:
102
120
  rubygems_version: 1.8.10