Extending Excavate
Overview
Adding a new format requires:
-
Creating an extractor class
-
Registering the extension
-
Adding tests
Step 1: Create Extractor Class
Create a new file in lib/excavate/extractors/:
# lib/excavate/extractors/rar_extractor.rb
module Excavate
module Extractors
class RarExtractor < Extractor
def extract(target)
# Validate archive
validate_archive!
# Extract using appropriate library
extract_rar(target)
# Return target path
target
end
private
def validate_archive!
# Check if file is valid RAR
# Raise UnknownArchiveError if not
end
def extract_rar(target)
# Implementation using library
# Ensure target directory is created
# Extract all files
end
end
end
endStep 2: Register Extension
Add to the TYPES hash in lib/excavate/archive.rb:
TYPES = {
# ... existing mappings
"rar" => Extractors::RarExtractor
}Step 3: Add Tests
Create test file in spec/extractors/:
# spec/extractors/rar_extractor_spec.rb
RSpec.describe Excavate::Extractors::RarExtractor do
let(:archive) { 'spec/fixtures/test.rar' }
let(:target) { Dir.mktmpdir }
describe '#extract' do
it 'extracts RAR archive' do
extractor = described_class.new(archive)
extractor.extract(target)
expect(Dir.glob("#{target}/*")).not_to be_empty
end
it 'raises error for invalid RAR' do
extractor = described_class.new('invalid.rar')
expect { extractor.extract(target) }
.to raise_error(Excavate::UnknownArchiveError)
end
end
endStep 4: Add Fixtures
Add test archives to spec/fixtures/:
spec/fixtures/
├── test.rar # Basic RAR archive
├── test_nested.rar # RAR with nested content
└── test_corrupt.rar # Corrupted RAR for error testingImplementation Guidelines
Error Handling
Raise appropriate errors:
def extract(target)
raise UnknownArchiveError, "Not a valid RAR file" unless valid?
raise TargetExistsError, target if File.exist?(target)
# Extraction logic
endCompound Format Support
For formats that may contain other archives:
class RarExtractor < Extractor
def extract(target)
# Extract RAR
extract_contents(target)
# Handle nested archives if recursive_packages
if @options[:recursive_packages]
extract_nested(target)
end
end
private
def extract_nested(target)
Dir.glob("#{target}/**/*").each do |file|
next unless File.file?(file)
next unless archive?(file)
# Recursively extract
Archive.new(file).extract(
File.dirname(file),
recursive_packages: true
)
end
end
end