safeguard 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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