Conversation
added 9 commits
February 24, 2026 17:09
Writer now accepts an external IO (Tempfile) rather than managing its own buffer. ZipKit::Streamer writes directly to that IO, avoiding loading the whole archive in memory. Reader gains a block form that yields a ZipFile::Reader::IO, a streaming IO wrapper around a single zip entry backed by ZipKit's extractor.
EpubArchiver#generate now writes to a Tempfile instead of StringIO, so large EPUBs are never held fully in memory. Archiver#write_epub streams the Tempfile into the ZIP entry via IO.copy_stream with compress: false — EPUBs are already ZIP-compressed, so double- compressing is wasteful and slow.
Register IO as an inflection acronym so Zeitwerk resolves reader/io.rb to ZipFile::Reader::IO instead of ZipFile::Reader::Io. Update Book::ArchiverTest to pass a ZipFile::Writer to generate and read back from the resulting Tempfile. Update EpubArchiverTest to expect a Tempfile from generate instead of StringIO.
ZipFile::Writer is now an IO-like proxy: write() forwards to the underlying IO while tracking byte_size and building an MD5 digest, and the streamer is wired through self so tracking is accurate. stream_to() and checksum() are restored. Book::Exportable#export uses the Writer+Tempfile pattern to match the updated Archiver#generate signature.
POST /books/:book_id/exports destroys any existing export for the current user, creates a new Book::Export record (status: pending), and enqueues Book::ExportJob. The job calls export.build, which streams the ZIP via ZipFile.create_for and attaches it with ActiveStorage. Status flows pending → processing → completed (or failed). Only the create action is exposed — download is via email. One export per user per book is kept.
Books::ExportMailer#ready sends a download link once the export job completes. Views live under app/views/mailers/ and ApplicationMailer uses append_view_path to find them there. The download link uses rails_blob_url so it works from any mailer context.
BooksController#edit loads the current user's most recent export. If it is completed, a download link showing the filename appears next to the export button in the footer nav.
A fixed-position toast fades in and out over 4 seconds using a CSS animation, with no JavaScript required. The layout renders it whenever a notice flash is set.
abbe88d to
3e1b6ab
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Async book export with email download link
A completed export's filename also appears as a download link on the edit page for convenience. A CSS-only flash toast (fixed-position, fades out after 4s) confirms the export was queued.