This repository has been archived on 2024-06-24. You can view files and clone it, but cannot push or open issues or pull requests.
ace-of-base/lib/ace_of_base/storage.rb

76 lines
2 KiB
Ruby
Raw Normal View History

2019-07-10 21:03:07 +12:00
# frozen_string_literal: true
require 'digest'
module AceOfBase
# Implements our database.
class Storage
include Enumerable
STORAGE_DIR = File.expand_path('../../data/', __dir__)
Error = Class.new(AceOfBase::Error)
def initialize(storage_dir = STORAGE_DIR)
@storage_dir = storage_dir
ensure_data_dir_exists_and_is_writable!
end
def store(record)
with_write_locked_file_for_record(record) do |file|
file.write(RecordSerialiser.encode(record))
end
end
def retrieve(project, shot, version)
with_read_locked_file(hash_for(project, shot, version)) do |file|
RecordSerialiser.decode(file.read)
end
end
def each
Dir['*', base: storage_dir].each do |path|
with_read_locked_file(path) do |file|
yield RecordSerialiser.decode(file.read)
end
end
end
private
attr_reader :storage_dir
def ensure_data_dir_exists_and_is_writable!
raise Error, format('Storage directory (%p) does not exist.', storage_dir) unless File.directory?(storage_dir)
raise Error, format('Storage directory (%p) is not writable.', storage_dir) unless File.writable?(storage_dir)
end
def with_write_locked_file_for_record(record)
target = File.join(storage_dir, hash_for(record.project, record.shot, record.version))
File.open(target, File::CREAT | File::WRONLY, 0o600) do |file|
raise Error, 'Unable to acquire exclusive lock.' unless file.flock(File::LOCK_EX | File::LOCK_NB)
result = yield file
file.flock(File::LOCK_UN)
result
end
end
def with_read_locked_file(path)
target = File.join(storage_dir, path)
File.open(target, File::RDONLY) do |file|
file.flock(File::LOCK_SH)
result = yield file
file.flock(File::LOCK_UN)
result
end
rescue Errno::ENOENT
raise Error, 'Record not found'
end
def hash_for(project, shot, version)
Digest::SHA1.hexdigest("#{project}|#{shot}|#{version}")
end
end
end