85 lines
2.1 KiB
Ruby
85 lines
2.1 KiB
Ruby
# typed: true
|
|
class BlobEntry < ReduxApplicationRecord
|
|
self.table_name = "blob_entries_p"
|
|
|
|
include ImmutableModel
|
|
before_destroy { raise ActiveRecord::ReadOnlyRecord }
|
|
|
|
self.primary_key = :sha256
|
|
EMPTY_FILE_SHA256 =
|
|
HexUtil.hex2bin(
|
|
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
)
|
|
|
|
belongs_to :base,
|
|
optional: true,
|
|
foreign_key: :base_sha256,
|
|
class_name: "::BlobEntry"
|
|
|
|
after_create do
|
|
actual_sha256 = Digest::SHA256.digest(contents)
|
|
raise("digest mismatch for BlobEntry") if sha256 != actual_sha256
|
|
end
|
|
|
|
def base
|
|
@base_model ||= super || self.class.ensure(base_sha256) if base_sha256
|
|
end
|
|
|
|
validates_presence_of(:sha256, :content_type, :size)
|
|
validates :contents,
|
|
length: {
|
|
minimum: 0,
|
|
allow_nil: false,
|
|
message: "can't be nil",
|
|
}
|
|
validates :sha256, length: { is: 32 }
|
|
validates :base_sha256, length: { is: 32 }, if: :base_sha256
|
|
|
|
def self.ensure(sha256)
|
|
find_by(sha256: sha256) ||
|
|
raise("blob #{HexUtil.bin2hex(sha256)} does not exist")
|
|
end
|
|
|
|
def sha256_hex
|
|
HexUtil.bin2hex(sha256) if sha256
|
|
end
|
|
|
|
def contents
|
|
@contents ||=
|
|
begin
|
|
contents_raw = self.read_attribute(:contents)
|
|
self.base ? XDiff.patch(self.base.contents, contents_raw) : contents_raw
|
|
end
|
|
end
|
|
|
|
def bytes_stored
|
|
self.read_attribute(:contents).size
|
|
end
|
|
|
|
def self.find_or_build(content_type:, contents:)
|
|
sha256 = Digest::SHA256.digest(contents)
|
|
BlobEntry.find_by(sha256: sha256) ||
|
|
begin
|
|
build_record(
|
|
content_type: content_type,
|
|
sha256: sha256,
|
|
contents: contents,
|
|
)
|
|
end
|
|
end
|
|
|
|
DIFFABLE_CONTENT_TYPES = [%r{text/html}, %r{text/plain}, %r{application/json}]
|
|
|
|
def self.build_record(content_type:, sha256: nil, contents:)
|
|
sha256 ||= Digest::SHA256.digest(contents)
|
|
record =
|
|
self.new(
|
|
sha256: sha256,
|
|
content_type: content_type,
|
|
size: contents.size,
|
|
contents: contents,
|
|
)
|
|
record
|
|
end
|
|
end
|