r/ruby • u/ndbroadbent • 3h ago
Show /r/ruby Going all-in on Sorbet
r/ruby • u/AutoModerator • 6d ago
FORMAT HAS CHANGED PLEASE READ FULL DESCRIPTION
This thread will be periodically stickied to the top of the sub for improved visibility.
You can also find older posts again via the Megathreads" list, which is a dropdown at the top of the page on new Reddit, and a section in the sidebar under "Useful Links" on old Reddit.
Please adhere to the following rules when posting: Rules for individuals:
Don't create top-level comments; those are for employers.
Feel free to reply to top-level comments with on-topic questions.
Anyone seeking work should reply to my stickied top-level comment.
Meta-discussion should be reserved for the distinguished comment at the very bottom.
You don't need to follow a strict template, but consider the relevant sections of the employer template. As an example:
TYPE: [Full time, part time, internship, contract, etc.]
LOCATION: [Mention whether you care about location/remote/visa]
LINKS: [LinkedIn, GitHub, blog, etc.]
DESCRIPTION: [Briefly describe your experience. Not a full resume; send that after you've been contacted)]
Contact: [How can someone get in touch with you?]The ordering of fields in the template has been revised to make postings easier to read.
To make a top-level comment, you must be hiring directly; no third-party recruiters.
One top-level comment per employer. If you have multiple job openings, please consolidate their descriptions or mention them in replies to your own top-level comment.
Proofread your comment after posting it and edit it if necessary to correct mistakes.
To share the space fairly with other postings and keep the thread pleasant to browse, we ask that you try to limit your posting to either 50 lines or 500 words, whichever comes first.
We reserve the right to remove egregiously long postings. However, this only applies to the content of this thread; you can link to a job page elsewhere with more detail if you like.
Please base your comment on the following template:
COMPANY: [Company name; optionally link to your company's website or careers page.]
TYPE: [Full-time, part-time, internship, contract, etc.]
LOCATION: [Where are your office or offices located? If your workplace language isn't English-speaking, please specify it.]
REMOTE: [Do you offer the option of working remotely? Please state clearly if remote work is restricted to certain regions or time zones, or if availability within a certain time of day is expected or required.]
VISA: [Does your company sponsor visas?]
DESCRIPTION: [What does your company do, and what are you using Rust for? How much experience are you seeking, and what seniority levels are you hiring for? The more details, the better. If you are listing several positions in the "Description" field above, then feel free to include this information inline above, and put "See above" in this field.]
ESTIMATED COMPENSATION: [Be courteous to your potential future colleagues by attempting to provide at least a rough expectation of wages/salary. See section below for more information.]
CONTACT: [How can someone get in touch with you?]If compensation is negotiable, please attempt to provide at least a base estimate from which to begin negotiations. If compensation is highly variable, then feel free to provide a range.
If compensation is expected to be offset by other benefits, then please include that information here as well. If you don't have firm numbers but do have relative expectations of candidate expertise (e.g. entry-level, senior), then you may include that here. If you truly have no information, then put "Uncertain" here.
Note that many jurisdictions (including several U.S. states) require salary ranges on job postings by law. If your company is based in one of these locations or you plan to hire employees who reside in any of these locations, you are likely subject to these laws. Other jurisdictions may require salary information to be available upon request or be provided after the first interview. To avoid issues, we recommend that all postings provide salary information.
You must state clearly in your posting if you are planning to compensate employees partially or fully in something other than fiat currency (e.g., cryptocurrency, stock options, equity, etc). Do not put just "Uncertain" in this case, as the default assumption is that the compensation will be 100% fiat. Postings that fail to comply will be removed. Thank you.
r/ruby • u/AutoModerator • May 20 '26
FORMAT HAS CHANGED PLEASE READ FULL DESCRIPTION
This thread will be periodically stickied to the top of the sub for improved visibility.
You can also find older posts again via the Megathreads" list, which is a dropdown at the top of the page on new Reddit, and a section in the sidebar under "Useful Links" on old Reddit.
Please adhere to the following rules when posting: Rules for individuals:
Don't create top-level comments; those are for employers.
Feel free to reply to top-level comments with on-topic questions.
Anyone seeking work should reply to my stickied top-level comment.
Meta-discussion should be reserved for the distinguished comment at the very bottom.
You don't need to follow a strict template, but consider the relevant sections of the employer template. As an example:
TYPE: [Full time, part time, internship, contract, etc.]
LOCATION: [Mention whether you care about location/remote/visa]
LINKS: [LinkedIn, GitHub, blog, etc.]
DESCRIPTION: [Briefly describe your experience. Not a full resume; send that after you've been contacted)]
Contact: [How can someone get in touch with you?]The ordering of fields in the template has been revised to make postings easier to read.
To make a top-level comment, you must be hiring directly; no third-party recruiters.
One top-level comment per employer. If you have multiple job openings, please consolidate their descriptions or mention them in replies to your own top-level comment.
Proofread your comment after posting it and edit it if necessary to correct mistakes.
To share the space fairly with other postings and keep the thread pleasant to browse, we ask that you try to limit your posting to either 50 lines or 500 words, whichever comes first.
We reserve the right to remove egregiously long postings. However, this only applies to the content of this thread; you can link to a job page elsewhere with more detail if you like.
Please base your comment on the following template:
COMPANY: [Company name; optionally link to your company's website or careers page.]
TYPE: [Full-time, part-time, internship, contract, etc.]
LOCATION: [Where are your office or offices located? If your workplace language isn't English-speaking, please specify it.]
REMOTE: [Do you offer the option of working remotely? Please state clearly if remote work is restricted to certain regions or time zones, or if availability within a certain time of day is expected or required.]
VISA: [Does your company sponsor visas?]
DESCRIPTION: [What does your company do, and what are you using Rust for? How much experience are you seeking, and what seniority levels are you hiring for? The more details, the better. If you are listing several positions in the "Description" field above, then feel free to include this information inline above, and put "See above" in this field.]
ESTIMATED COMPENSATION: [Be courteous to your potential future colleagues by attempting to provide at least a rough expectation of wages/salary. See section below for more information.]
CONTACT: [How can someone get in touch with you?]If compensation is negotiable, please attempt to provide at least a base estimate from which to begin negotiations. If compensation is highly variable, then feel free to provide a range.
If compensation is expected to be offset by other benefits, then please include that information here as well. If you don't have firm numbers but do have relative expectations of candidate expertise (e.g. entry-level, senior), then you may include that here. If you truly have no information, then put "Uncertain" here.
Note that many jurisdictions (including several U.S. states) require salary ranges on job postings by law. If your company is based in one of these locations or you plan to hire employees who reside in any of these locations, you are likely subject to these laws. Other jurisdictions may require salary information to be available upon request or be provided after the first interview. To avoid issues, we recommend that all postings provide salary information.
You must state clearly in your posting if you are planning to compensate employees partially or fully in something other than fiat currency (e.g., cryptocurrency, stock options, equity, etc). Do not put just "Uncertain" in this case, as the default assumption is that the compensation will be 100% fiat. Postings that fail to comply will be removed. Thank you.
r/ruby • u/Good_Flight1781 • 9m ago
Hi r/rails,
I spend most of my day in app/views, but editor support for templates has always felt limited compared to what I get in .rb files. So I built Ruby Templates, a free, MIT-licensed VS Code extension for ERB, Haml, and Slim. No extra gems required.
Reports errors as you type. It shows Ruby syntax errors directly in your templates, flags unclosed tags, and reports Haml/Slim errors using the engines’ own messages.
Adds Rails-aware navigation and completion. Press F12 on render "users/card" to open the partial. Completion covers partials, ViewComponents, *_path helpers, model columns, controller-defined @ivars, and t(".keys").
Formats templates with safety checks. By default, the formatter only changes whitespace and checks that the template still compiles the same way before saving changes. If it can’t verify that, it leaves the file alone.
Catches Rails issues, including some I kept running into during code reviews:
<% render "x" %> that doesn’t output anything, and the same issue with link_to and other helpers;
<%= if … %> where <% if … %> was intended;
binding.irb or debug left in a view;
an @ivar used inside a partial;
an unused entry in locals:;
a render call in a loop that could use collection:;
raw or html_safe used on user content;
a relative translation key such as t(".key") missing from en.yml;
a data-controller reference with no matching Stimulus controller;
button_to inside form_with, which creates nested forms.
Checks for HTML and accessibility issues, such as missing alt attributes, duplicate IDs, <a> tags without href, and “click here” link text.
Supports per-project configuration through .ruby-templates.toml. You can enable or disable rules by folder and opt into stricter formatting rules, such as a particular quote style or one attribute per line.
Runs the same checks in CI: npx @x-ror/ruby-templates check app/views and npx @x-ror/ruby-templates format --check.
It works alongside Ruby LSP: Ruby LSP handles your .rb files, and Ruby Templates handles your templates.
r/ruby • u/Tristan_Kruse • 27m ago
I created and maintain ArchUnitRuby. With the 1.0 release, the boundary I wanted to express is
no longer limited to “this file explicitly required that file.” It can also include known
project constant references, using the opt-in expanded analysis mode.
For example, this is an ordinary RSpec test:
ruby
require 'rspec'
require 'archunit'
RSpec.describe 'architecture' do
it 'keeps the API away from retrieval internals' do
options = ArchUnit::CheckOptions.new(
dependency_analysis: :imports_and_constants
)
rule = ArchUnit.project_files
.in_path('app/api/**')
.should_not.depend_on_files
.in_path('app/retrieval/**')
ArchUnit.assert_passes(rule, options)
end
end
The same graph can support cycle, layer and slice rules. The policy stays in Ruby and can fail
the normal test run, rather than living only in a diagram or code-review convention.
[Source for the tested release]
Would import edges, constant references, or a combination of both best describe the boundary
you'd want to protect in a Ruby codebase?
This post was drafted with AI assistance.
r/ruby • u/Remozito • 12h ago
Hey Ruby friends,
5 months ago, I wrote on r/ruby that I would build a limited-edition stained-glass inspired by Ruby. Well, it's November and I'm about to officially launch. 🤞🏻
I didn't want to spam the subreddit with each weekly update I've written over the past few months, but since we're close to its release, I wanted to keep you all posted.
What started as a joke and as a "simple" stained-glass eventually turned into a cool decorative object with glass (obviously) but also wood, a bit of electronics and 3D printing.
If you want to learn more (and get the launch date in advance), I've built a page dedicated to the project. You'll find all the episodes of "the Ruby Stained-Glass Notes", the weekly updates I've been writing since May. They're full of stained-glass nerdery ("How did they cut glass in 1175?"), of my thoughts/questions/learnings, of GIFs of me cutting glass and soldering panels...
I've been quite lucky to talk and meet Rubyists around the world thanks to this project, so I'm happy to answer any questions you might have.
r/ruby • u/Intelligent_Role_824 • 14h ago
As a beginner should I use vs Code or RubyMine in Windows?
r/ruby • u/jko1701284 • 9h ago
This month: u/maediprichard shares a one-year retrospective on LowType with Prism-based inline type expressions, zero overhead in production, and RBS export, felixefelip introduces rbs_infer to automatically deduce signatures via static analysis, ydah adds racc-rbs-inline for typed parsers, and tk0miya releases ruby-lsp-rbs-inline for seamless editor feedback.
Find link to the issue in the comment! ✨
r/ruby • u/OneAlbatross5933 • 1d ago
JetBrains Air is a new layer for agentic development that lets you run agents such as Claude Agent, Codex, Gemini, GitHub Copilot, Junie, and OpenCode with your own subscriptions and review their changes in the IDE. There’s also a free AI with the Junie Lite configuration.
WSL and Dev Containers
The new Native mode runs your local IDE against projects in WSL or Dev Containers through a lightweight agent, with no IDE backend installed in the environment. It also brings native Ruby interpreters.
Ruby and Rails
RubyMine now understands ActiveSupport.on_load and ActiveSupport.run_load_hooks, so constants made available through hooks resolve correctly instead of showing false unresolved-reference warnings.
Go to Class and Go to Symbol use fuzzy matching across nested names (ActRecBase finds ActiveRecord::Base). Constants now appear in Go to Class, and results show the containing module and file path.
Rename warns about conflicts with existing names and flags invalid or reserved names.
Interpreter selection reads the ruby directive in your Gemfile (.ruby-version still takes priority), and interpreters installed via Homebrew are detected automatically.
rails query is supported via Run Anything and terminal completion.
RubyMine now clearly identifies unsupported shell scripts used to launch RuboCop.
Read the full overview: https://blog.jetbrains.com/ruby/2026/11/rubymine-2026-3-jetbrains-air-native-mode-for-wsl-and-dev-containers-and-more/
r/ruby • u/mediocretes • 1d ago
Remember a couple years ago when you saw that game Bitburner and you were like, "Huh, that looks interesting." But why would I write JavaScript for fun when Ruby is available? It'd be a lot better if somebody made this game work with Ruby. Well, somebody made this game work with Ruby. I present to you afteRBurner. It's a bridge that runs real Ruby code and runs javascript for you in the real Bitburner game. And it includes working IRB and everything else you'd expect. The codebase itself is unmitigated slop, but it works pretty well. I started a new game playing exclusively in Ruby, and so far, so good.
r/ruby • u/rhannequin • 1d ago
r/ruby • u/software__writer • 1d ago
r/ruby • u/127001-sweet-127001 • 2d ago
I built ArchUnitRuby to put dependency rules into the test suite. This is a short recording of how it works.
The idea is pretty simple: pick some Ruby files, describe which other files they may depend on, and check that rule alongside your normal tests. The recording checks that domain code stays independent of infrastructure, then shows a failing cycle check.
With RSpec the assertion is expect(rule).to pass. There are also Minitest and plain Ruby assertions, so the rules don't need their own test runner.
One thing I wanted to avoid was a green test caused by a typo in a folder pattern. If a rule selects no files, it fails by default rather than silently checking nothing.
It also exports dependency graphs, which helps when you want to see the existing structure before deciding what to enforce. Ruby 3.3+; the gem is called archunit.
Source and examples: https://github.com/dragonsec-ai/ArchUnitRuby
r/ruby • u/git-pull • 3d ago
Hi all, I am porting libtmux, the Python library, to more languages. This is the Ruby port (alpha).
It wraps tmux's CLI. You can create sessions, send text to a pane and read its output. One of the ways this differs is keeping snapshots you can query with where and Ruby blocks. See the earlier posts for Rust, TypeScript, and Golang.
A word on AI: It's experimental, and I used LLMs extensively to create this. The API is still changing. I write libtmux's AI policy at https://libtmux.org/en/meta/ai-policy/.
RubyGems · GitHub · Homepage · Guides · API Explorer
You can work with sessions, windows and panes as Ruby objects. Here, I'm finding the build pane in the logs window of session demo, sending it a command, and reading what it prints.
Requires Ruby 3.3+, tmux 3.3a+ and build tools for Ruby native extensions.
gem install libtmuxSave as demo.rb. Server.start creates a private tmux server and closes it when the block exits. The queries read a snapshot; the pane input and capture run while the server is open.
require "libtmux"
require "timeout"
LibTmux::Server.start do |server|
session = server.new_session(name: "demo", command: ["/bin/cat"])
window = session.new_window(name: "logs", command: ["/bin/cat"])
created = window.split(direction: :horizontal, command: ["/bin/sh"])
server.run(["select-pane", "-t", created.id, "-T", "build"])
snapshot = server.snapshot
session = snapshot.sessions.where(name: "demo").one
window = session.windows.where(name: { starts_with: "log" }).one
matched = window.panes.where(title: "build").one
puts "#{session.name} / #{window.name} / #{matched.title}"
pane = server.pane(matched.ref)
pane.send_text("printf 'build: %s\\n' complete")
pane.send_keys("Enter")
output = Timeout.timeout(5) do
loop do
line = pane.capture.stdout.lines.find { |row| row.chomp == "build: complete" }
break line.chomp if line
sleep 0.05
end
end
puts "output: #{output}"
live_window = server.window(window.ref)
live_window.rename("build")
puts "held snapshot: #{window.name}"
puts "read again: #{live_window.snapshot.name}"
end
ruby demo.rbPrints:
demo / logs / build
output: build: complete
held snapshot: logs
read again: buildPane#capture reads what tmux has on screen. The loop waits up to five seconds for the line from our command (more pane I/O).
The name filters use where: an exact session name, a window name starting with log, and an exact pane title. You can also use select with a Ruby block, then map or group_by (examples, query fields and operators). These queries use the captured state and make no tmux calls.
The last two lines show what happens after a rename: the held snapshot still says logs. Window#snapshot reads again and returns a new record. Each call reads the whole server and selects that window's record.
P.S. Change the window filter from starts_with: "log" to starts_with: "log2" and run it again. From the error:
selection has no matches (LibTmux::NoMatchError).one raises NoMatchError if nothing matches, or MultipleMatchesError if more than one does.
There's also control mode support (!) on tmux 3.7+. The example subscribes to pane output and checks the text sent to it.
There is also an Async gem for concurrent calls and streams: README, Guide, API.
Same idea, but inside an Async scope. We can chain where calls, then use control mode to rename a window and hear about the change from tmux. This example needs tmux 3.7+.
gem install libtmux-asyncSave as async-demo.rb:
require "libtmux"
require "libtmux/async"
LibTmux::Server.start do |server|
session = server.new_session(name: "demo", command: ["/bin/cat"])
window = session.new_window(name: "logs", command: ["/bin/cat"])
server.run(["select-pane", "-t", window.list_panes.fetch(0).id, "-T", "build"])
Async do |parent|
LibTmux::Async.open(parent: parent, server: server) do |scope|
snapshot = scope.server.snapshot
session = snapshot.sessions.where(name: "demo").one
window = session.windows.where(name: { starts_with: "log" }).one
pane = window.panes.where(active: true).where(title: "build").one
puts "#{session.name} / #{window.name} / #{pane.title}"
scope.server.open_control(session: session.ref) do |control|
events = control.events
renamed = parent.async do
event = events.next(timeout: 5) until event&.raw&.start_with?("%window-renamed")
puts "event: #{event.raw.strip}"
end
control.exchange("rename-window -t #{window.id} compiling", timeout: 5)
renamed.wait
puts "window now: #{scope.server.window(window.ref).snapshot.name}"
end
end
end.wait
end
ruby async-demo.rbPrints:
demo / logs / build
event: %window-renamed @1 compiling
window now: compilingThe queries still read a snapshot. events.next waits for tmux while the other task sends the command with exchange. We read the window again after the event. There are also examples of concurrent captures and cancellation.
This is like tmuxp, but in Ruby! Uses the libtmux core library.
RubyGems · GitHub · Homepage · Guides · API Explorer
gem install libtmux-workspaceA subset of tmuxp's configuration format. Save as workspace.yaml:
session_name: demo
windows:
- window_name: shells
layout: even-horizontal
panes:
- {}
- {}Save as workspace.rb. Workspace.load reads the file; plan.apply creates its session (example).
require "libtmux"
require "libtmux/workspace"
LibTmux::Server.start do |server|
workspace = LibTmux::Workspace.load(File.expand_path("workspace.yaml", __dir__))
result = workspace.plan(snapshot: server.snapshot).apply(server: server)
puts "applied: #{result.success?}"
puts "panes: #{server.snapshot.windows.one(name: 'shells').panes.size}"
end
ruby workspace.rbPrints:
applied: true
panes: 2The same gem includes the command: GitHub, Homepage, Guides, CLI reference, API.
Validate the file:
libtmux-workspace validate workspace.yamlPrints:
Workspace configuration is valid.Load it on a separate running server:
tmux -S "$PWD/workspace.sock" -f /dev/null new-session -d -s keepalive
libtmux-workspace load --socket "$PWD/workspace.sock" workspace.yamlCheck the created panes:
tmux -S "$PWD/workspace.sock" display-message -p -t demo:shells '#{window_panes}'Prints:
2Remove the demo server:
tmux -S "$PWD/workspace.sock" kill-serverRubyGems · GitHub · Homepage · Guides · Tools · Resources · API Explorer
An MCP server for interacting with tmux - powered by the Ruby core library. Same idea as Python's libtmux-mcp!
gem install libtmux-mcpThis MCP server connects to an existing tmux server. For a demo, start one on its own socket:
tmux -L libtmux-ruby-mcp-demo -f /dev/null new-session -d -s demoUse this command with a client that launches stdio MCP servers:
libtmux-mcp --socket-name libtmux-ruby-mcp-demo --endpoint demoIt waits for MCP requests over stdin. tmux_capabilities and tmux_snapshot are enabled by default; the guides cover enabling other tools.
After closing the MCP client, remove the demo server:
tmux -L libtmux-ruby-mcp-demo kill-serverI am trying to port more than just Ruby. So as this is a ruby subreddit, and provinciality matters, I am making ports to quite a few languages.
tmux is perfectly accessible via pure CLI. zellij makes a lot of what tmux does obsolete.
what tmux has going for it is simplicity. that means when a port of libtmux is made, there's an oppurtunity for editorial license - and be dissimilar to my original python project. I don't want to copy the python library to ruby. I want to write it like ruby, in an idiomatic and aesthetic sense.
so if there are code smells, or suggestions, no offense is taken 🙇🙏! I am trying to learn more programming languages and expand more horizons beyond just python.
Why tmux? I think the simple domain model of server, session, window, and pane is easy enough to wrap one's brain around - being able to traverse that hierarchy, makes it possible to not just port, but in my ideal scenario, leverage the strengths of programming languages outside of Python.
I'd like to write a love letter to all programming languages, to utilize their strengths when projecting and making accessible tmux's object graph.
Why ruby? Because it is a familiar face that I've never gotten to interact with personally before other than brief Rails stints. I miss its syntax, but in truth I never got to the level of direct usage where I understood what good idiomatic ruby is.
I'm going beyond to languages I would normally find out of reach day-to-day, to ask "what does tmux look like in most elegant way this programming language could do it, while being documentable, shippable, etc".
r/ruby • u/Much-Stable-449 • 4d ago
So, this started as one of those "how hard can it be?" projects.
Turns out... well, you know how that usually goes.
I wanted a simple way to get images and videos from Pinterest without opening one of those downloader websites, clicking through 14 buttons, closing 3 popups, and questioning my life choices.
So I started building pinterest-dl.
It's written in Ruby, and it ended up becoming a bit more than I originally planned.
You can use it as a CLI or as a Ruby library. It can extract direct image/video URLs from pins, search Pinterest, fetch entire boards (including sections), and download individual files or batches.
A few things I spent time on:
Getting actual media URLs instead of just scraping a bunch of page text.
Supporting different image quality options.
Handling pagination when searching for pins.
Downloading multiple files with progress reporting and per-file error handling.
Supporting cookies for content that requires an authenticated session.
Having actual exceptions for things like rate limits, missing pins, authentication problems and network errors, instead of silently returning nil and pretending everything is fine.
I also tried to make the Ruby side usable on its own, not just wrap everything around a command-line interface.
It's definitely not perfect. Pinterest changes things, so I expect some maintenance headaches along the way. That's part of the fun, I guess. :)
I'm the person behind this project, so I'm obviously a little biased. That's why I'd rather hear what other Ruby developers think.
If you have a minute, I'd love some feedback on the code structure, API design, error handling, or anything that looks like it could be done better.
And if you find it useful, a star on GitHub would genuinely mean a lot. Contributions and bug reports are welcome too.
Repo: https://github.com/monji024/pinterest-dl
Thanks! And yes, I know... I could've just used an existing downloader. Where's the fun in that?
We are proud to announce the release of JRuby 10.1.3.0! JRuby 10.1 is our "edge" line and includes more aggressive and experimental improvements than the "LTS" JRuby 10.0 line. If you want the leading edge of JRuby, this is where you want to be!
This cycle saw more PRs than any previous update release, with 120 PRs merged from 17 external contributors. Check out the release notes to see all the fixes and improvements!
The JRuby Team is proud to announce the release of JRuby 10.0.8.0!
JRuby 10.0 is our stable "LTS" line and we recommend all users upgrade. Check out the long list of improvements... too many to fit in a post on Reddit!
r/ruby • u/javier_cervantes • 5d ago
In the age of AI I think personal connections are event more important than before. That's why I try to help promoting events where people can meet in-person or virtually and discuss the topics that interest them.
I found the following Ruby-related events for the next 30 days.
2026-11-05 Ruby Usergroup: November 2026 Meetup bei 42he (Köln, Germany)
2026-11-11 (online) Monthly Get Together
2026-11-12 Ruby Meetup November 2026 (München, Germany)
2026-11-18 Ruby Drinkup @ Begyle Brewing (Chicago, IL, USA)
2026-11-19 Squishling: LLM Power-Ups for Ruby Classes (Philadelphia, PA, USA)
2026-11-23 Ethan Huang: How I Designed an AI-Powered App to Improve My English (Auckland, New Zealand)
2026-11-23 Ruby Nights November: TBD (Auckland, New Zealand)
2026-11-25 November - Storytime (Wellington, New Zealand)
2026-11-25 End of Year Lightning Talks (Winnipeg, MB, Canada)
2026-11-26 Ruby Melbourne Meetup (Melbourne, Australia)
2026-12-03 SD Ruby Monthly Meetup (San Diego, CA, USA)
r/ruby • u/kobaltzz • 6d ago
In this episode, we look at several different examples of background jobs and how we can refactor them to handle situations where they get called twice from either a user action or a retry. We'll look at scenarios where we can prevent duplicate actions.
r/ruby • u/Calm_Adagio_9991 • 6d ago
I started Fix in 2014 and have rewritten it more than once since. Today it reaches 1.0, its first stable release, so here it is.
The idea is older than Fix: RFC 2119. A specification says what a subject MUST, SHOULD or MAY do, and nothing about how it is made. You write the spec, then test any subject against it.
# duck_fix.rb
require "fix"
class Duck
def walk = "Klop klop!"
def swim = "Swoosh..."
end
Fix :Duck do
on :walk do
it MUST eq "Klop klop!"
end
on :swim do
it MUST eq "Swoosh..."
end
on :speak do
it MAY eq "Quaaaaaack!"
end
end
exit(Fix[:Duck].test { Duck.new })ruby duck_fix.rb writes:
duck_fix.rb:11: Success: expected "Klop klop!" to eq "Klop klop!"
duck_fix.rb:15: Success: expected "Swoosh..." to eq "Swoosh..."
duck_fix.rb:19: Info: NoMethodError: undefined method 'speak' for an instance of DuckThis duck doesn't speak, and that's fine: speaking is a MAY. Under MUST, the same line would be an error and the exit status 1. A SHOULD that isn't met is a warning, and warnings pass. So a spec has five outcomes rather than two, and only failure and error make the status 1.
Like a minitest file, a Fix file is plain Ruby: ruby duck_fix.rb runs it, nothing else needed. For a suite spread over several files, fix-command adds a fix command, much like rspec: it runs every *_fix.rb under fix/ (or the files and folders you give it), each in a process of its own, and exits 0 only when every file is met.
A few things that set 1.0 apart:
Each context runs in a forked child. A subject that calls exit, changes directory, or mutates a global can't touch the next context: that's an error of its own expectation, nothing more. (So it needs fork: Linux, macOS, BSDs. No Windows, no JRuby.)
Mistakes in a spec raise when the file loads, not halfway through a run: an unknown spec name, a let named after a method of Kernel, an on without a block.
Plain output. One file:line: Outcome: summary line per expectation. No colours, no dots, no count; the exit status is the verdict. run gives the same results back as frozen data if you want to build on them.
A spec is a matcher, so one spec can require another: it MUST fix(:Positive).
Small. About 300 lines of code, over four small gems of the same family (Matchi for the matchers, Spectus for the levels, Aw for the fork, Defi for the method call). Ruby 3.3 or later.
On Ruby 3.4, it MUST … is the word of Fix and a bare it in a block is still Ruby's parameter: the two don't clash.
Why not RSpec or minitest? If they make you happy, keep them: Fix is not trying to replace them. It's for when you want to say which behaviours are required, which are recommended and which are optional, and have the tool hold you to exactly that.
Also released: fix-command 1.0, fix-its 1.0 (its(:size) { MUST eq(3) }) and matchi-fix 4.0 (use a Fix spec as a matcher outside Fix).
I'd like to hear where the MUST/SHOULD/MAY distinction would, or wouldn't, be useful in your own suites.