Added zipper

Wraps `Zip::OutputStream` to create an in memory zip file in order to answer a monad result for further processing.

Milestone: minor
This commit is contained in:
Brooke Kuhlmann
2026-05-18 10:07:00 -06:00
parent 70bfcc6cf8
commit 4e51cd170c
2 changed files with 73 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
# frozen_string_literal: true
require "dry/monads"
require "initable"
require "refinements/string"
require "zip"
module Terminus
module Aspects
# A monadic compressor of zip file content.
class Zipper
include Initable[io: Zip::OutputStream]
include Dry::Monads[:result]
using Refinements::String
def self.compress manifest, buffer
manifest.each do |name, content|
buffer.put_next_entry name
buffer.write content
end
end
def call manifest
io.write_buffer { self.class.compress manifest, it }
.tap(&:rewind)
.then { Success it }
rescue TypeError, Zip::Error => error
Failure error.message.up
end
end
end
end
+40
View File
@@ -0,0 +1,40 @@
# frozen_string_literal: true
require "hanami_helper"
RSpec.describe Terminus::Aspects::Zipper do
subject(:zipper) { described_class.new }
describe "#call" do
let(:manifest) { {"one.txt" => "One", "two.txt" => "Two"} }
it "create zip file in memory" do
io = zipper.call(manifest).value!
content = Zip::File.open_buffer(io).each.with_object({}) do |entry, attributes|
attributes[entry.name] = entry.get_input_stream.read
end
expect(content).to eq(manifest)
end
it "answers StringIO instance" do
expect(zipper.call(manifest)).to match(Success(kind_of(StringIO)))
end
it "answers failure with invalid type" do
expect(zipper.call({bogus: Object.new})).to be_failure(
"No implicit conversion of Object into String"
)
end
it "answers failure with zip error" do
io = class_double Zip::OutputStream
zipper = described_class.new(io:)
allow(io).to receive(:write_buffer).and_raise(Zip::Error, "Danger!")
expect(zipper.call({test: "test"})).to be_failure("Danger!")
end
end
end