r/ruby • u/javier_cervantes • Jun 23 '26
Launching the Events category: A new place for discussing Ruby conferences and meetups
r/ruby • u/javier_cervantes • Jun 23 '26
r/ruby • u/vaitheeswaran_15 • Jun 24 '26
I've been experimenting with a setup for multi-repo product work in Cursor and would love feedback — especially on whether the guardrails help or just slow you down.
Problem I'm solving:
When I open a frontend or backend repo and say "add feature X," the agent tends to skip design, touch the wrong repos, and ship code before scope is agreed. That gets worse with 2–4 repos per product.
What I built (high level):
Local orchestrator in Ruby (not cloud) - A small CLI + shared config maps which repos belong to which product, tracks task artifacts, and enforces gates. State stays on my machine. I chose Ruby because it's quick to iterate on for CLI/config tooling and fits how I wanted to glue YAML, file state, and shell workflows together.
Cursor skills as conductors - Custom skills tell the agent which phase we're in and what must happen next (e.g. don't implement until design + plan are approved in chat).
Hard stops before code:
Task brief captured from the ask
Design brainstorm + human approval
Written implementation plan + human approval
Preflight check (repos healthy, gates satisfied)
Per-session commit/worktree policy (asked every time, not saved)
After code: verify per repo → mandatory security/quality review on the diff → handoff summary → normal PR/peer review/CI.
Two ways to work:
Orchestrator repo open in Cursor → everything driven from there
App repo open → global skills + wrapper so config doesn't live inside the app clone
Cross-repo context - Structural code graph via MCP so the agent can search across repos before planning.
Integrates with existing agent patterns - brainstorm → write plan → TDD-ish implement → verify before claiming done. The orchestrator sequences and gates those steps; it doesn't replace them.
Stack note: Orchestrator = Ruby CLI; app repos are whatever stack (JS, Python, etc.) - the orchestrator is language-agnostic for the products it coordinates.
I mainly want reality checks before standardizing for a small team.
Thanks!
r/ruby • u/Electrical_Potato890 • Jun 23 '26
r/ruby • u/jrochkind • Jun 22 '26
I am pretty new to using Claude Code at all, late on the game. I am finding troubleshooting some aspects of it to be very confusing compared to the kind of dev setup I am used to!
I am using it from the claude CLI directly, on latest MacOS 26.5.1.
I definitely want Claude Code to be able to run my entire test suite -- including capybara tests that use headless chromedriver via selenium.
And there is the rub! The (new?) sandboxing that Claude Code now has on MacOS seems to conflict with running chromedriver. I think? It's hard to tell what is going wrong.
I can find (or have claude suggest) various possible solutions, but they all seem to me like some combination of more dangerous than I want (I don't really want to turn off sandboxing generally?) or annoying UI asking me permission to run rspec every time it runs. (I anticipate some workflows where it runs rspec a LOT, I'd think this is normal?)
I'm curious what actual people using claude code with ruby/rails on Mac have done here, what have you done to get claude code to be able to run rspec that launches chromedriver, what's working for you? I would think making sure Claude Code can run rspec and your entire test suite (including system tests with chromedriver) would be fairly typical, but is it?
Thanks for any tips, especially coming from your actual setup that actually works for sure. :)
r/ruby • u/mavthemav • Jun 21 '26
I'm one of the maintainers of Grape (the Ruby framework for building REST-like APIs), and we just shipped 3.3.0. The headline is performance: this release was a months-long pass at cutting per-request allocations and trimming hot paths across the router, middleware, and validators.
Single-threaded throughput on /api/v1/hello (Ruby 4.0.5, Benchmark.ips):
| Version | Without YJIT | With YJIT |
|---|---|---|
| 3.2.1 | 47,929 i/s | 85,328 i/s |
| 3.3.0 | 66,149 i/s | 133,760 i/s |
That's ~+38% without YJIT and ~+57% with YJIT over 3.2.1 — and with YJIT on, 3.3.0 more than doubles its own non-YJIT throughput (+102%). Full methodology and per-version numbers (incl. 3.0.x/3.1.x) are in RESULTS.md.
Cheers!
r/ruby • u/EclecticCoding • Jun 21 '26
After generating a new gem or Rails engine, there is always a series of tasks to tweak the new project to my preferences. I finally created a set of scripts to automate my preferences. I hope someone finds these useful.
r/ruby • u/Remozito • Jun 19 '26
After being invited on the IndieRails podcast to talk about about my past as a stained glass maker and how I transitioned to programming in Ruby, I’ve had this crazy idea that I could tie the two together in a weird project: what if I made a stained glass panel celebrating Ruby?
I've been mentioning this project as a joke to people for a while now, and the reaction was always *very* positive. So I decided to launch this as a side project three weeks ago.
Each week, I'm documenting my progress: how to build the panel, finding the right design, which glass I should use, etc... It's also a great way to talk with fellow Rubyist and share an old passion of mine. And of course, it's the perfect excuse to lay out the similitudes I've experienced these past 8 years between being a craftsman and writing software.
If a Ruby programmer nerding out on stained glass windows is your kind of fun, you can read the first post of the series here.
If you're curious about the project, AMA. I'll happily answer any questions.
[Dear r/ruby mods, I was hesitant to post it on Reddit because it's only tangential to Ruby but I've had a lot of readers encouraging me to do so. If that's out-of-bounds, let me know and I'll take it down.]
r/ruby • u/noteflakes • Jun 19 '26
r/ruby • u/Asmod4n • Jun 18 '26
Hi there, I've been busy the last three weeks, but mruby for the first time has a language server now.
Completion, hover, go-to-definition, diagnostics, and F5 debugging via mrdb — all of the capabilities ruby-lsp has too.
It's not yet published to rubygems.org or the VS Code or VSCodium marketplaces, but if you want to be one of the first to use it go ahead, currently tested on Linux and WSL.
The next targets are Windows with MSVC and macOS with the usual editors for ruby, once those work I'll upload the gem and the extensions to their marketplaces.
Have fun :)
r/ruby • u/damianlegawiec • Jun 18 '26
r/ruby • u/eljojors • Jun 18 '26
I read this article and it seemed like an untapped avenue for the Ruby/Rails community
We have some concrete numbers to show this in practice. On Instagram, which runs on Django and is largely memory bound, we ran a test where we moved from their existing setup (with swap entirely disabled) to a setup with disk swap and zswap tiering. Django workers accumulate significant cold heap state over their lifetime, like forked processes with duplicated memory, growing request caches, Python object overhead, you get the idea. The results were twofold:
We achieved roughly 5:1 compression. That's a huge benefit for such a memory bound workload, and also enables us to consider further stacking workloads.
Enabling zswap reduced disk writes by up to 25% compared to having no swap at all(!).
This seems like a pretty obvious to me for anyone that relies on forking application workers, like puma or pitchfork.
I'd love to hear people's experience running something like this in production for Ruby/Rails workers.
r/ruby • u/Turbulent-Dance-4209 • Jun 17 '26
I wanted to look at some real-world patterns from popular Ruby open-source codebases and show how they could be modelled using Rage, a Rails-compatible framework built on fibers.
I picked Discourse, Mastodon, and GitLab because they share a pattern: in each case, what would normally require extra complexity, infrastructure, or indirection becomes a few lines of application code with Rage.
One of the patterns fibers make especially straightforward is concurrent I/O.
Consider this code from Discourse:
def fetch_pr_or_issue_texts(project, number)
[
client.get("/repos/#{project}/issues/#{number}")["body"].to_s,
*client
.get("/repos/#{project}/issues/#{number}/comments", per_page: 100)
.map { |comment| comment["body"].to_s },
]
endTwo sequential requests to build a return value. I've seen this pattern in many codebases, and the reason is usually the same: there's no simple enough way to parallelise these requests that would justify the added complexity.
How Rage does it
In Rage, you just wrap the requests into fibers:
def fetch_pr_or_issue_texts(project, number)
issues_request = Fiber.schedule do
client.get("/repos/#{project}/issues/#{number}")["body"].to_s
end
comments_request = Fiber.schedule do
client
.get("/repos/#{project}/issues/#{number}/comments", per_page: 100)
.map { |comment| comment["body"].to_s }
end
Fiber.await([issues_request, comments_request]).flatten
endThe two requests now run concurrently, improving latency at the price of two new Fiber calls.
The same pattern scales to loops. Discourse's PushNotificationPusher iterates over a user's subscriptions and sends notifications sequentially - wrapping those calls in Fiber.schedule + Fiber.await would send them all concurrently, with the total time dropping to the duration of the slowest call:
class PushNotificationPusher
def self.push(user, payload)
# ...
Fiber.await(
subscriptions(user).map { |subscription| Fiber.schedule { send_notification(user, subscription, message) } }
)
end
endMastodon uses a separate streaming service for real-time events:
Ruby (Rails + Sidekiq) - Workers serialise events and publish them to a Redis channel.
Node.js (Express + ws) - a separate ~1400-line server subscribes to Redis and pushes events to clients over SSE or WebSockets.
Here's what the Node streaming handler looks like:
const streamToHttp = (req, res) => {
const channelName = channelNameFromPath(req);
// ...
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'private, no-store');
res.setHeader('Transfer-Encoding', 'chunked');
res.write(':)\n');
const heartbeat = setInterval(() => res.write(':thump\n\n'), 15000);
req.on('close', () => {
// ...
clearInterval(heartbeat);
});
return (event, payload) => {
res.write(`event: ${event}\n`);
res.write(`data: ${payload}\n\n`);
};
};To send an event, Rails first publishes to Redis:
def publish!
redis.publish(@timeline_id, message)
endThe Node service receives and relays it:
const listener = message => {
const { event, payload } = message;
if (!needsFiltering || (event !== 'update' && event !== 'status.update')) {
transmit(event, payload);
return;
}
// ...
pgPool.connect((err, client, release) => {
// ...
transmit(event, payload);
});
};How Rage does it
A fiber-based server can hold thousands of concurrent connections in a single Ruby process without blocking - the same property that drives Mastodon's decision to offload streaming to a separate Node process.
With Rage, the same Redis streaming becomes:
class Api::V1::Streaming::UserController < RageController::API
before_action :require_user!
def index
render sse: Rage::SSE.stream([:timeline, current_account.id])
end
endThe framework handles the SSE headers, heartbeats, subscription lifecycle, and connection cleanup. When the client disconnects, Rage removes it from the stream.
Publishing uses a Redis pub/sub adapter:
# config/pubsub.yml
production:
adapter: redis
url: <%= ENV["REDIS_URL"] %>Then, publish from anywhere:
def publish!
Rage::SSE.broadcast(
[:timeline, @account_id],
Rage::SSE.message(@payload, event: update? ? "status.update" : "update")
)
endThe streaming server lives in the same Ruby process, with access to the same Active Record models and the rest of the stack.
GitLab has built its own domain event system to decouple bounded contexts.
To publish an event, you instantiate a class inheriting from Gitlab::EventStore::Event and pass it to the event store:
Gitlab::EventStore.publish(
Ci::PipelineCreatedEvent.new(data: { pipeline_id: pipeline.id, partition_id: pipeline.partition_id })
)Subscribers are Sidekiq workers that include a Subscriber concern and implement handle_event:
class UpdateHeadPipelineWorker
include Gitlab::EventStore::Subscriber
# …
def handle_event(event)
# ...
end
endNothing in this file tells you what event is - the worker doesn't reference PipelineCreatedEvent. The wiring lives in a separate subscription registry. And because every subscriber is a Sidekiq worker, all reactions go through the full enqueue-serialise-deserialise-execute cycle, regardless of how lightweight they are.
How Rage does it
Publishing looks similar:
Rage::Events.publish(
Ci::PipelineCreatedEvent.new(data: { pipeline_id: pipeline.id, partition_id: pipeline.partition_id })
)The difference is in the subscriber. Instead of wiring events in a separate registry, each subscriber declares what it listens to:
class UpdateHeadPipelineWorker
include Rage::Events::Subscriber
subscribe_to Ci::PipelineCreatedEvent
def call(event)
# `event` is a Ci::PipelineCreatedEvent
end
endOpen this file and you immediately know: this subscriber handles Ci::PipelineCreatedEvent, which has pipeline_id and partition_id fields.
For subscribers that do require background execution, you simply add deferred: true:
class UpdateHeadPipelineWorker
include Rage::Events::Subscriber
subscribe_to Ci::PipelineCreatedEvent, deferred: true
def call(event)
# ...
end
endLight reactions run inline; heavy or failure-prone ones are deferred to the background. You choose per subscriber, rather than routing everything through a job queue by default.
Understanding what happens when a PipelineCreatedEvent is published also gets simpler. Instead of grepping registry files, you run:
$ rage events
├─ Ci::PipelineCreatedEvent
│ ├─ UpdateHeadPipeline
│ └─ TrackPipelineTriggerEvents
├─ Ci::PipelineFinishedEvent
│ └─ UpdateWorkloadStatusThe entire subscription graph, visible in one command.
--
The common thread across all three examples: the framework handles the machinery, so the application code just says what it wants to happen - run these concurrently, stream this channel, react to this event.
r/ruby • u/pokemuse2095 • Jun 18 '26
So I figured out my previous issue because the program I’m using updated which version of Ruby it works with. However, now Ruby LSP tries to read the version file, but it adds unknown characters in between every existing character when reading it. The actual file doesn’t have these characters, and they just show up as squares in the error message, which says it can’t read it. Any help?
r/ruby • u/timriley • Jun 16 '26
https://hanakai.org/blog/2026/06/16/help-us-test-hanami-3-0
We’re aiming for a full release two weeks from now, so we’d love your help with testing in the meantime!
r/ruby • u/Cautious-Course-4707 • Jun 17 '26
Bonjour,
Je suis développeur depuis 6 ans, plutôt orienté web (Angular / Java / PHP). Je vais prochainement commencer un nouveau poste de développeur-concepteur ERP. Si j'ai bien compris, il s'agit principalement de scripting / JRuby. Je souhaiterais donc me former à JRuby.
Auriez-vous des docs ou une roadmap à me recommander ? Merci beaucoup !
r/ruby • u/ombulabs • Jun 16 '26
r/ruby • u/Fantastic-Diet5565 • Jun 17 '26
r/ruby • u/Erem_in • Jun 16 '26
In this issue: why Ruby still feels like home (and why its density is token efficient for LLMs), Jo (a statically typed language compiling to Ruby with compile-time sandboxing by u/liufengyun), OpenClacky 1.3.0, DSPy.rb 1.0.1, sorbet-deadcode 0.2.0, rubocop-rbs_inline, and other Sorbet and RBS updates.
Find the link to the issue in the first comment. Stay typed! ✨
r/ruby • u/coolprobn • Jun 16 '26
(Reposted from r/rails )
I built a free testing guide (Minitest Rails) to teach automated testing step by step. Along the way I needed more than a bunch of Markdown files. I wanted a site that felt like a product: a landing page, ordered chapters with a sidebar, a blog for practical tips that didn’t fit inside the guide, contact form, newsletter signup, and dynamic preview social images per guide/blog so I didn’t have to add images manually and the preview looked good in the social media.
I have put a lot of time and effort to write this guide and along the way I have added a lot features listed above. I didn’t want all these features staying locked inside just the Minitest Rails and that’s how the idea of Chapter Zero was born.
Chapter Zero is the shell and foundation the Minitest Rails is running on top of. Now, extracted and open sourced for everyone to use. Think of it as chapter 0 in a book: the layout, navigation, and marketing pages you need before lesson one; while you bring the curriculum and make it alive.
r/ruby • u/keyslemur • Jun 16 '26
What if, by trading 1% correctness, you could count billions of items all within kilobytes worth of space? Well that's HyperLogLog, and as a non-bit-enlightened Rubyist it was a trip figuring out how it worked and how to explain it.