Claude Code fixes. StandardRB

This commit is contained in:
Dan Milne
2025-06-26 18:03:04 +10:00
parent 289aba72ab
commit e6278363a8

View File

@@ -6,12 +6,35 @@ require "uri"
module Moviehash
class Error < StandardError; end
class FileNotFoundError < Error; end
class NetworkError < Error; end
CHUNK_SIZE = 64 * 1024 # in bytes
class InvalidInputError < Error; end
DEFAULT_CHUNK_SIZE = 64 * 1024 # in bytes
DEFAULT_TIMEOUT = 30 # seconds
HASH_MASK = 0xffffffffffffffff
class << self
attr_writer :chunk_size, :timeout
def chunk_size
@chunk_size || DEFAULT_CHUNK_SIZE
end
def timeout
@timeout || DEFAULT_TIMEOUT
end
def configure
yield self if block_given?
end
end
def self.compute_hash(url)
validate_input(url)
data = url.start_with?("http") ? data_from_url(url) : data_from_file(url)
hash = data[:filesize]
@@ -23,73 +46,93 @@ module Moviehash
end
def self.data_from_file(path)
filesize = File.size(path)
raise FileNotFoundError, "File not found: #{path}" unless File.exist?(path)
raise FileNotFoundError, "Path is a directory: #{path}" if File.directory?(path)
begin
filesize = File.size(path)
data = {filesize: filesize, chunks: []}
File.open(path, "rb") do |f|
data[:chunks] << f.read(CHUNK_SIZE)
f.seek([0, filesize - CHUNK_SIZE].max, IO::SEEK_SET)
data[:chunks] << f.read(CHUNK_SIZE)
data[:chunks] << f.read(chunk_size)
f.seek([0, filesize - chunk_size].max, IO::SEEK_SET)
data[:chunks] << f.read(chunk_size)
end
data
rescue Errno::EACCES
raise FileNotFoundError, "Permission denied: #{path}"
rescue Errno::ENOENT
raise FileNotFoundError, "File not found: #{path}"
rescue => e
raise Error, "Failed to read file #{path}: #{e.message}"
end
end
def self.data_from_url(url)
uri = URI(url)
raise NetworkError, "Invalid URL scheme: #{uri.scheme}" unless %w[http https].include?(uri.scheme)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == "https")
http.read_timeout = timeout
http.open_timeout = timeout
# Get the file size
response = http.request_head(uri.path)
filesize = response["content-length"].to_i
raise NetworkError, "HTTP #{response.code}: #{response.message}" unless response.code == "200"
filesize = response["content-length"]&.to_i
raise NetworkError, "Server did not provide content-length header" unless filesize && filesize > 0
data = {filesize: filesize, chunks: []}
# Process the beginning of the file
response = http.get(uri.path, { "Range" => "bytes=0-#{CHUNK_SIZE - 1}" })
response = http.get(uri.path, {"Range" => "bytes=0-#{chunk_size - 1}"})
raise NetworkError, "Failed to fetch beginning chunk: HTTP #{response.code}" unless response.code.start_with?("2")
data[:chunks] << response.body
# Process the end of the file
start_byte = [0, filesize - CHUNK_SIZE].max
start_byte = [0, filesize - chunk_size].max
response = http.get(uri.path, {"Range" => "bytes=#{start_byte}-#{filesize - 1}"})
raise NetworkError, "Failed to fetch ending chunk: HTTP #{response.code}" unless response.code.start_with?("2")
data[:chunks] << response.body
data
rescue URI::InvalidURIError => e
raise NetworkError, "Invalid URL: #{e.message}"
rescue Net::OpenTimeout, Net::ReadTimeout => e
raise NetworkError, "Request timeout: #{e.message}"
rescue SocketError => e
raise NetworkError, "Network error: #{e.message}"
rescue => e
raise NetworkError, "Failed to fetch from URL: #{e.message}"
end
def self.process_chunk(chunk, hash)
return hash unless chunk
chunk.unpack("Q*").each do |n|
hash = hash + n & 0xffffffffffffffff
hash = hash + n & HASH_MASK
end
hash
end
end
def self.old_compute_hash(path)
filesize = File.size(path)
hash = filesize
private
format("%016x", hash)
def self.validate_input(input)
raise InvalidInputError, "Input cannot be nil" if input.nil?
raise InvalidInputError, "Input cannot be empty" if input.empty?
raise InvalidInputError, "Input must be a string" unless input.is_a?(String)
# Read 64 kbytes, divide up into 64 bits and add each
# to hash. Do for beginning and end of file.
File.open(path, "rb") do |f|
# Q = unsigned long long = 64 bit
f.read(CHUNK_SIZE).unpack("Q*").each do |n|
hash = hash + n & 0xffffffffffffffff # to remain as 64 bit number
if input.start_with?("http")
begin
uri = URI(input)
raise InvalidInputError, "Invalid URL: missing host" unless uri.host
rescue URI::InvalidURIError => e
raise InvalidInputError, "Invalid URL: #{e.message}"
end
format("%016x", hash)
f.seek([0, filesize - CHUNK_SIZE].max, IO::SEEK_SET)
# And again for the end of the file
f.read(CHUNK_SIZE).unpack("Q*").each do |n|
hash = hash + n & 0xffffffffffffffff
end
format("%016x", hash)
end
format("%016x", hash)
end