brandonhilkert/sucker_punch
Sucker Punch is a Ruby asynchronous processing library using concurrent-ruby, heavily influenced by Sidekiq and girl_friday.
Healthy across all four use cases
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 5mo ago
- ✓25+ active contributors
- ✓MIT licensed
Show 4 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Slowing — last commit 5mo ago
- ⚠Concentrated ownership — top contributor handles 70% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/brandonhilkert/sucker_punch)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/brandonhilkert/sucker_punch on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: brandonhilkert/sucker_punch
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/brandonhilkert/sucker_punch shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 5mo ago
- 25+ active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Slowing — last commit 5mo ago
- ⚠ Concentrated ownership — top contributor handles 70% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live brandonhilkert/sucker_punch
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/brandonhilkert/sucker_punch.
What it runs against: a local clone of brandonhilkert/sucker_punch — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in brandonhilkert/sucker_punch | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 167 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of brandonhilkert/sucker_punch. If you don't
# have one yet, run these first:
#
# git clone https://github.com/brandonhilkert/sucker_punch.git
# cd sucker_punch
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of brandonhilkert/sucker_punch and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "brandonhilkert/sucker_punch(\\.git)?\\b" \\
&& ok "origin remote is brandonhilkert/sucker_punch" \\
|| miss "origin remote is not brandonhilkert/sucker_punch (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "lib/sucker_punch.rb" \\
&& ok "lib/sucker_punch.rb" \\
|| miss "missing critical file: lib/sucker_punch.rb"
test -f "lib/sucker_punch/job.rb" \\
&& ok "lib/sucker_punch/job.rb" \\
|| miss "missing critical file: lib/sucker_punch/job.rb"
test -f "lib/sucker_punch/queue.rb" \\
&& ok "lib/sucker_punch/queue.rb" \\
|| miss "missing critical file: lib/sucker_punch/queue.rb"
test -f "lib/sucker_punch/async_syntax.rb" \\
&& ok "lib/sucker_punch/async_syntax.rb" \\
|| miss "missing critical file: lib/sucker_punch/async_syntax.rb"
test -f "lib/sucker_punch/railtie.rb" \\
&& ok "lib/sucker_punch/railtie.rb" \\
|| miss "missing critical file: lib/sucker_punch/railtie.rb"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 167 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~137d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/brandonhilkert/sucker_punch"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Sucker Punch is a single-process Ruby asynchronous job processing library built on concurrent-ruby that eliminates the need for external infrastructure (Redis, databases, message queues). Jobs execute in-memory within the same application process using thread pools, making it ideal for non-critical, fast async tasks like logging and email without the operational overhead of systems like Sidekiq. Modular single-package structure: lib/sucker_punch/ contains core classes (job.rb, queue.rb, counter.rb), syntax helpers (async_syntax.rb, core_ext.rb), a Rails integration layer (railtie.rb), and a testing mode (testing/inline.rb). lib/generators/ provides Rails job scaffolding. Tests mirror the main structure in test/sucker_punch/.
👥Who it's for
Ruby/Rails developers deploying to constrained environments (like Heroku free tier) who need background job processing for non-mission-critical work but want to avoid running separate job queue infrastructure or paying for additional dynos.
🌱Maturity & risk
Production-ready and actively maintained. The repo shows v3.0 is released with backwards compatibility, has a full CI/CD pipeline (GitHub Actions in .github/workflows/build.yml), comprehensive test suite (test/ directory), and Code Climate integration. However, commit frequency appears moderate rather than frequent, suggesting stable maintenance rather than heavy active development.
Low risk for appropriate use cases. Key constraints: all jobs are in-memory only, so process restart loses unfinished jobs (unsuitable for mission-critical work). Single maintainer (brandonhilkert) creates sustainability risk. Dependency chain is shallow (primarily concurrent-ruby). Breaking changes are explicitly avoided (v3.x backwards-compatible with v2.x), though older pre-2.0 syntax requires opt-in compatibility layer.
Active areas of work
No specific active development milestones visible in provided data, but the repository maintains v3.0 release with backwards compatibility and CI/CD verification. The stable state suggests maintenance focus over feature development.
🚀Get running
git clone https://github.com/brandonhilkert/sucker_punch.git
cd sucker_punch
bundle install
bundle exec rake test
Daily commands:
No dev server to start. Run tests with bundle exec rake test. For Rails integration, include gem 'sucker_punch' in Gemfile and define job classes as shown in README. Jobs enqueue via YourJob.perform_async(args) or sync via YourJob.new.perform(args).
🗺️Map of the codebase
lib/sucker_punch.rb— Main entry point and top-level API that initializes the gem and exposes core configuration; every contributor must understand the public interface here.lib/sucker_punch/job.rb— Core Job abstraction that all async work inherits from; defines the contract and behavior for every background task in Sucker Punch.lib/sucker_punch/queue.rb— In-memory queue implementation that manages worker pools and job execution; the heart of the concurrent-ruby integration.lib/sucker_punch/async_syntax.rb— Provides the syntactic sugar (.async.method_name) that makes Sucker Punch ergonomic; critical for understanding the DSL.lib/sucker_punch/railtie.rb— Rails integration layer that hooks Sucker Punch into the Rails lifecycle; essential for Rails app maintainers.lib/sucker_punch/testing.rb— Testing utilities and inline execution mode; required reading for writing tests and understanding test behavior.
🧩Components & responsibilities
- Job (Ruby class, method definitions) — User-defined class inheriting from SuckerPunch::Job; implements perform method with async work logic.
- Failure mode: Exception in perform is caught and logged; job fails silently unless user adds error handling.
- Queue (concurrent-ruby ThreadPoolExecutor, Thread-safe data structures) — In-memory queue backed by concurrent-ruby ThreadPoolExecutor; manages worker pool size, job enqueue/dequeue, and worker lifecycle.
- Failure mode: If queue is at capacity, new jobs block or reject (configurable); on app shutdown, unprocessed jobs are lost.
- AsyncSyntax (Ruby method_missing, proxy pattern) — Syntactic sugar layer that provides
.asyncmethod chaining and enqueues jobs without blocking.- Failure mode: If queue is full or down, enqueue may raise or block; malformed job definitions raise at enqueue time.
- Railtie (Rails Railtie, at_exit hook) — Hooks into Rails initialization and shutdown; sets up queues and drains them before process termination.
- Failure mode: If shutdown hook times out, process is forcibly terminated; remaining jobs in queue are lost.
🔀Data flow
Rails controller/action→AsyncSyntax proxy— User callsMyJob.async.perform(args)to enqueue workAsyncSyntax proxy→Queue/ThreadPoolExecutor— Job instance is enqueued into the appropriate queueQueue/ThreadPoolExecutor→Worker thread— Worker thread dequeues job and invokes perform methodWorker thread→Job.perform— Async work executes in background thread; caller returns immediatelyJob.perform→Counter/metrics— Job completion or exception is recorded for monitoring
🛠️How to make changes
Create a new async Job
- Use Rails generator or manually inherit from SuckerPunch::Job in lib/jobs/ or app/jobs/ (
lib/generators/sucker_punch/templates/job.rb) - Define a
perform(*args)method containing your async logic (lib/sucker_punch/job.rb) - Invoke the job asynchronously using
YourJob.async.perform(args)in your controller or service (lib/sucker_punch/async_syntax.rb)
Configure queue concurrency and size
- Call SuckerPunch.configure in config/initializers/sucker_punch.rb with queue options (
lib/sucker_punch.rb) - Set min_threads, max_threads, and idletime on individual queues (
lib/sucker_punch/queue.rb)
Enable synchronous execution for tests
- Call SuckerPunch::Testing.inline! in your test helper or setup (
lib/sucker_punch/testing.rb) - Jobs will execute synchronously in the same process during tests (
lib/sucker_punch/testing/inline.rb)
Integrate with Rails application lifecycle
- Add gem 'sucker_punch' to Gemfile and bundle install (
sucker_punch.gemspec) - Rails Railtie automatically loads on require; shutdown hook gracefully drains queues on app termination (
lib/sucker_punch/railtie.rb)
🔧Why these technologies
- concurrent-ruby — Provides thread-safe, efficient ThreadPoolExecutor for in-process job concurrency without external dependencies like Redis.
- In-memory queue (no persistence) — Trades durability for simplicity and cost; ideal for non-critical async work (mail, notifications) in single-process deployments like Heroku.
- Rails Railtie — Hooks Sucker Punch into Rails lifecycle for automatic initialization and graceful shutdown without boilerplate in initializers.
⚖️Trade-offs already made
-
In-memory queue with no persistence
- Why: Reduces operational complexity and hosting costs; no need for external message broker.
- Consequence: Jobs are lost if the process crashes or is force-killed; unsuitable for critical, must-not-lose work.
-
Single-process concurrency via threads
- Why: Simpler than multi-process solutions; lower memory footprint on platforms like Heroku.
- Consequence: Does not scale horizontally; each process has its own isolated queue; no job sharing across instances.
-
Thread-based workers in a single process
- Why: Lower overhead than spawning new processes for each job.
- Consequence: GIL implications for CPU-bound work; better suited for I/O-bound tasks (network, DB).
🚫Non-goals (don't propose these)
- Distributed job processing across multiple servers or processes
- Job persistence or durability guarantees on process failure
- Job retry logic with exponential backoff (out of scope; user responsibility)
- Real-time job monitoring or web UI for job introspection
- Support for job dependencies or workflows
⚠️Anti-patterns to avoid
- Swallowed exceptions in job execution (Medium) —
lib/sucker_punch/queue.rb: Exceptions in job.perform are caught and logged but not re-raised; users may not notice failing jobs without explicit error handling. - Global queue state via class variables (Low) —
lib/sucker_punch.rb: Queues are stored in a class-level hash and mutated globally; difficult to test and reason about state isolation. - Method missing for async proxy creation (Low) —
lib/sucker_punch/async_syntax.rb: Relies on method_missing for dynamic proxy; harder to debug and introspect than explicit method delegation.
🔥Performance hotspots
lib/sucker_punch/queue.rb(undefined) — ThreadPoolExecutor bounded by max_threads; if max_threads is too low or jobs are slow, queue backs
🪤Traps & gotchas
In-memory data loss: If the Rails process terminates (deploy, crash, etc.), any unprocessed jobs in queues are permanently lost—not suitable for critical workflows. Thread safety: While concurrent-ruby handles thread pools, user-defined job perform methods must be thread-safe if they access shared state. Testing mode: Must explicitly enable SuckerPunch::Testing.inline! in test setup to run jobs synchronously; forgetting this causes test/prod behavior mismatch. Worker sizing: Default 2 workers per job; no automatic scaling based on queue depth.
🏗️Architecture
💡Concepts to learn
- Thread pool executor — Sucker Punch's entire job execution model is built on concurrent-ruby's thread pools (one per job class); understanding fixed-size thread pools is essential for predicting behavior under load.
- In-memory queue with data loss on restart — Unlike Redis or database-backed queues, Sucker Punch trades durability for simplicity; this architectural choice fundamentally limits safe use cases to non-critical tasks.
- Method object pattern / async syntax sugar — Sucker Punch uses
method_missinginasync_syntax.rbto provide fluent async interface (LogJob.new.async.perform); this pattern demonstrates Ruby metaprogramming for API design. - Rails Railtie integration — The
railtie.rbfile shows how to hook into Rails initialization lifecycle; understanding this is essential for writing Rails-aware Ruby libraries. - Inline testing mode — Sucker Punch's testing.rb provides a testing mode that runs jobs synchronously instead of asynchronously; this pattern solves the common problem of testing async code in a predictable way.
- Job enqueue interface (perform_async) — The perform_async class method pattern mirrors Active Job conventions; understanding how this is metaprogrammatically added to job classes (via Job module inclusion) is key to extending Sucker Punch.
🔗Related repos
ruby-sidekiq/sidekiq— Industry-standard Redis-backed background job processor; Sucker Punch is explicitly 'heavily influenced by Sidekiq' per the description but trades distributed scale for single-process simplicity.rylandg/girl_friday— Another inspiration cited in the repo description; in-process job queue using thread pools, predecessor to Sucker Punch's design philosophy.ruby-concurrency/concurrent-ruby— The foundational dependency powering Sucker Punch's thread pool and concurrency primitives; understanding this library is essential for debugging queue behavior.rails/rails— Rails Active Job provides the interface pattern that Sucker Punch integrates with via its railtie for seamless Rails application integration.jondot/sneakers— Alternative Ruby async job processor using RabbitMQ; represents the distributed-queue alternative if Sucker Punch's in-memory limitation becomes a constraint.
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive integration tests for async_syntax.rb with real job execution
The test file test/sucker_punch/async_syntax_test.rb exists but likely lacks coverage for edge cases in lib/sucker_punch/async_syntax.rb. The async syntax is a core feature that needs robust testing for method chaining, argument passing, and queue routing in various scenarios (success, failure, timeouts). This prevents regressions in the primary user-facing API.
- [ ] Review existing async_syntax_test.rb to identify coverage gaps
- [ ] Add tests for method_missing behavior with various argument types
- [ ] Add tests for async method chaining with multiple queue scenarios
- [ ] Add tests for exception handling during async job invocation
- [ ] Add tests for async syntax with class methods vs instance methods
Add GitHub Actions workflow for testing against multiple Ruby versions
The .github/workflows/build.yml exists but based on the repo structure, it likely tests only one Ruby version. Sucker Punch depends on concurrent-ruby and should be verified against Ruby 2.7, 3.0, 3.1, and 3.2+ to ensure compatibility across versions. This prevents shipping versions that break for users on different Ruby releases.
- [ ] Review current .github/workflows/build.yml configuration
- [ ] Expand build.yml to use matrix strategy with Ruby versions: 2.7, 3.0, 3.1, 3.2, 3.3
- [ ] Ensure gemspec Ruby version requirements are documented and tested
- [ ] Add separate test runs for Rails integration if applicable
- [ ] Verify concurrent-ruby compatibility across Ruby versions in the workflow
Add testing helpers documentation and examples in lib/sucker_punch/testing.rb
The testing module exists (lib/sucker_punch/testing.rb and lib/sucker_punch/testing/inline.rb) but there's no clear documentation on how to use it in tests. The README snippet doesn't mention testing utilities. Contributors and users need explicit examples of how to use inline/synchronous mode for test suites, which is critical for writing reliable tests with async jobs.
- [ ] Review lib/sucker_punch/testing.rb and inline.rb to understand current testing API
- [ ] Add inline mode usage examples to README.md with before/after test setup
- [ ] Add example in test/test_helper.rb showing recommended testing configuration
- [ ] Document how to test job enqueue calls vs actual execution
- [ ] Add integration test example showing Rails test setup with Sucker Punch
🌿Good first issues
- Add integration test for queue capacity limits:
test/sucker_punch/queue_test.rbappears to test basic queue behavior, but lacks tests for max queue size enforcement or overflow behavior. Add tests verifying the max_size parameter behavior. - Document worker pool configuration options: The README shows basic usage but doesn't explain how to configure worker thread count per job (implied by 'default number of workers is 2'). Add concrete examples to README with code snippets for common configurations.
- Add test coverage for Counter class:
lib/sucker_punch/counter.rbexists buttest/sucker_punch/counter_test.rbmay lack edge cases. Review test file and add tests for concurrent increments/decrements across multiple threads.
⭐Top contributors
Click to expand
Top contributors
- @brandonhilkert — 70 commits
- [@Jerry D'Antonio](https://github.com/Jerry D'Antonio) — 5 commits
- @olimart — 2 commits
- @aried3r — 2 commits
- @bethesque — 1 commits
📝Recent commits
Click to expand
Recent commits
98fefa2— require minitest/mock (#256) (olimart)2c05de4— Bump v3.3.0 (brandonhilkert)0340da7— Update Ruby versions and checkout action in build.yml (#255) (olimart)30aeaac— Update Changelog (brandonhilkert)92227f6— Bum po v3.2.0 (brandonhilkert)d0f1007— fix: rename singleton_class? to sucker_punch_is_singleton_class? to avoid naming class with core ruby method (#253) (bethesque)c5d94d0— Add Ruby 3.2 to CI. Update checkout action version. (#254) (petergoldstein)381480d— Wait for queues to be idle. (#251) (metaskills)a6e8cd3— Don't run test suite on ruby head (brandonhilkert)10800b6— Use latest version of setup ruby GH action plugin (brandonhilkert)
🔒Security observations
Sucker Punch is a relatively lightweight asynchronous job processing library with a limited attack surface. No critical vulnerabilities were identified from the file structure analysis. Primary concerns are around dependency management, input validation in job processing, and proper isolation of testing modes. The codebase would benefit from explicit security policies and comprehensive input validation documentation. The library's architecture as a concurrent-ruby wrapper reduces some security concerns inherent in distributed job systems, but users must still be careful with job parameter serialization and validation.
- Medium · Dependency on concurrent-ruby without version pinning —
Gemfile / sucker_punch.gemspec. The codebase relies on concurrent-ruby as a core dependency. Without access to the Gemfile or gemspec content, it's unclear if version constraints are properly specified. Using unversioned or loosely versioned dependencies can lead to unexpected behavior from upstream updates. Fix: Review and pin dependency versions appropriately using semantic versioning constraints (e.g., '~> 1.2.0'). Regularly audit dependencies for security updates. - Medium · Job processing without apparent input validation —
lib/sucker_punch/job.rb, lib/sucker_punch/queue.rb. The library processes asynchronous jobs (lib/sucker_punch/job.rb, lib/sucker_punch/queue.rb). Without visible input validation or sanitization mechanisms in the file structure, there's a risk of injecting malicious payloads through job arguments. Fix: Implement strict input validation and type checking for all job parameters. Document expected data types and validate arguments before processing. Consider serialization schemes that prevent code execution. - Low · Testing mode may bypass security controls —
lib/sucker_punch/testing/inline.rb, lib/sucker_punch/testing.rb. The presence of lib/sucker_punch/testing/inline.rb suggests a testing mode that may execute jobs inline instead of asynchronously. If testing configuration is used in production, it could expose security mechanisms designed for async processing. Fix: Ensure testing modes are strictly isolated to test environments. Implement environment-based configuration checks to prevent testing mode from being enabled in production. Use environment variables or Rails environment checks. - Low · Missing CHANGELOG security disclosure policy —
Repository root. While CHANGES.md exists, there's no indication of a security policy or responsible disclosure process for reporting vulnerabilities in the library. Fix: Create a SECURITY.md file documenting how users should report security vulnerabilities privately. Consider adopting a coordinated disclosure policy. - Low · Code generation via Job Generator —
lib/generators/sucker_punch/job_generator.rb, lib/generators/sucker_punch/templates/job.rb. The lib/generators/sucker_punch/job_generator.rb creates code templates. If user input is incorporated into generated code without proper escaping, there could be template injection risks. Fix: Review the generator to ensure all user-supplied parameters are properly escaped and validated. Use Rails generators' built-in safety mechanisms.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.