Class: Fingerprint::Checker

Inherits:
Object
  • Object
show all
Defined in:
lib/fingerprint/checker.rb

Overview

Given two fingerprints (master and copy) ensures that the copy has at least everything contained in master: For every file in the master, a corresponding file must exist in the copy. This means that there may be extraneous files in the copy, but ensures that every file in the master has been replicated accurately.

At this time, this implementation may require a large amount of memory, proportional to the number of files being checked.

Master and copy are IO objects corresponding to the output produced by Fingerprint::Scanner.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(master, copy, **options) ⇒ Checker

Returns a new instance of Checker.



34
35
36
37
38
39
# File 'lib/fingerprint/checker.rb', line 34

def initialize(master, copy, **options)
  @master = master
  @copy = copy
  
  @options = options
end

Instance Attribute Details

#copyObject (readonly)

Returns the value of attribute copy.



42
43
44
# File 'lib/fingerprint/checker.rb', line 42

def copy
  @copy
end

#failuresObject (readonly)

A list of files which either did not exist in the copy, or had the wrong checksum.



96
97
98
# File 'lib/fingerprint/checker.rb', line 96

def failures
  @failures
end

#masterObject (readonly)

Returns the value of attribute master.



41
42
43
# File 'lib/fingerprint/checker.rb', line 41

def master
  @master
end

Class Method Details

.check_files(master, copy, **options, &block) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/fingerprint/checker.rb', line 98

def self.check_files(master, copy, **options, &block)
  # New API that takes two RecordSets...
  
  File.open(master) do |master_file|
    File.open(copy) do |copy_file|
      master_recordset = RecordSet.new
      master_recordset.parse(master_file)
      
      copy_recordset = RecordSet.new
      copy_recordset.parse(copy_file)

      verify(master_recordset, copy_recordset, **options, &block)
    end
  end
end

.verify(master, copy, **options, &block) ⇒ Object

Helper function to check two fingerprint files.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/fingerprint/checker.rb', line 115

def self.verify(master, copy, **options, &block)
  error_count = 0 

  errors = options.delete(:recordset) || RecordSet.new
  if options[:output]
    errors = RecordSetPrinter.new(errors, options[:output])
  end

  checker = Checker.new(master, copy, **options)

  checker.check do |record, result, message|
    error_count += 1

     = {
      'error.code' => result,
      'error.message' => message
    }

    if result == :addition
      .merge!(record.)
      
      errors << Record.new(:warning, record.path, )
    elsif (copy = checker.copy.paths[record.path])
      changes = record.diff(copy)

      changes.each do |name|
        ["changes.#{name}.old"] = record[name]
        ["changes.#{name}.new"] = copy[name]
      end

      errors << Record.new(:warning, record.path, )
    else
      errors << Record.new(:warning, record.path, )
    end
  end

  if error_count
    summary_message = "#{error_count} error(s) detected."
  else
    summary_message = "No errors detected"
  end

  errors << Record.new(:summary, summary_message, {
    'error.count' => error_count
  })

  return error_count
end

Instance Method Details

#check(&block) ⇒ Object

Run the checking process.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/fingerprint/checker.rb', line 45

def check(&block)
  # For every file in the src, we check that it exists
  # in the destination:
  total_count = @master.records.count
  processed_size = 0
  total_size = @master.records.inject(0) { |count, record| count + (record['file.size'] || 0).to_i }
  
  if @options[:additions]
    copy_paths = @copy.paths.dup
  else
    copy_paths = {}
  end
  
  @master.records.each_with_index do |record, processed_count|
    copy_paths.delete(record.path)

    if @options[:progress]
      $stderr.puts "# Checking: #{record.path}"
    end

    next if record.mode != :file

    result, message = @copy.compare(record)
    if result != :valid
      yield record, result, message
    elsif @options[:extended]
      # Extended check compares other attributes such as user, group, file modes.
      changes = record.diff(copy.paths[record.path])
      
      if changes.size > 0
        yield record, :attribute_changed, "Attribute(s) #{changes.join(', ')} changed"
      end
    end
    
    if @options[:progress]
      $stderr.puts "# Progress: File #{processed_count} / #{total_count}; Byte #{processed_size} / #{total_size} = #{sprintf('%0.2f%', processed_size.to_f / total_size.to_f * 100.0)}"

      processed_size += (record['file.size'] || 0).to_i
    end
  end
  
  if @options[:additions]
    copy_paths.each do |path, record|
      next unless record.mode == :file || record.mode == :directory
      
      yield record, :addition, "File added"
    end
  end
end