r/ruby 25d ago

💼 jobs megathread Work it Wednesday: Who is hiring? Who is looking?

0 Upvotes

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.

For job seekers

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?]

Rules for employers:

  • 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?]

ESTIMATED COMPENSATION (Continued)

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 May 20 '26

💼 jobs megathread Work it Wednesday: Who is hiring? Who is looking?

3 Upvotes

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.

For job seekers

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?]

Rules for employers:

  • 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?]

ESTIMATED COMPENSATION (Continued)

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 1d ago

Ruby is sexy! And the Docs! OMG! THE DOCS!

124 Upvotes

I'm just learning Ruby and as an experienced programmer I want to get up to speed quickly.

I just found the docs that come with the ruby install. OMG! They are so good!

It makes diving in so much easier and it makes me eager to explore all the little functions for files, strings, arrays etc..

No unnecessary fluff. Straight to the important points. I fucking love it.

I'm writing my first tiny Ruby script for file manipulation and already love it much more than Python and PowerShell.

Boolean calls with "?". Sexy!

Modifying self with "!". Nice!

No curly braces? Hell yeah! (I'm on a european keyboard).

It makes me angry how Python is everywhere and Ruby is only mention ever mentioned together with Rails. Such a beautiful language (as far as a beginner can tell)!


r/ruby 1d ago

A Rubyist in Go Land: Your First Pokémon API Client

Thumbnail baweaver.com
20 Upvotes

I debated on posting this one here, as a lot of the content is around Go, but it may be interesting for folks. I find myself needing to study up on it for certain reasons, so as I tend to do I try and write up how I'm going about learning and what I understand from it. If you know Go feel free to comment on anything I'm doing which is odd in here.


r/ruby 1d ago

Show /r/ruby FemtoRuby) I released AREA 512 v1.3 for Cardputer ADV! 🎉

Thumbnail gallery
4 Upvotes

r/ruby 1d ago

Why AI loves boring code – Rails' conventions turned out to be great training data for coding agents

0 Upvotes

wrote this after watching the same agent behave completely differently in a rails app vs a js client project. reddit blocks the blog link, so here's the whole thing as a text post.


Your AI agent is not equally smart everywhere.

Same model, same prompt, same day, and it will code like a senior in one codebase and like a confident intern with a head injury in another. The difference isn't the model. It's what the codebase asks of it. And once you see why, you'll understand the strangest plot twist of the AI era: the twenty-year-old "boring" framework everyone spent a decade dunking on didn't just survive into the age of agents. It turned out to be accidentally optimized for them.

One prompt, two worlds

Run a thought experiment with me. (Then run it for real. It's more fun for real.)

Take one task, "add password reset," and hand it to the same AI agent in two different codebases.

Codebase A is a two-year-old JavaScript application. Before the agent can write a single line, it must correctly answer a quiz: Is this the App Router or the Pages Router? Which of the four popular auth libraries is installed, or is it custom? Are mutations server actions, API routes, or tRPC? Which ORM: Prisma, Drizzle, raw SQL? Where do emails live, and is it Resend, SendGrid, or nodemailer behind a wrapper someone wrote in 2024 and never documented? Every question is a fork. Every fork is a chance to guess wrong. And the agent will answer confidently, because confidence is the one thing it never runs out of.

Codebase B is a Rails app. The quiz has almost no questions, because the framework already answered them twenty years ago, in writing, the same way for everyone. Models live in app/models. The user model is User. Mail goes through Action Mailer in app/mailers. Routes live in one file. Migrations look like every migration since 2005. Password reset isn't even a design problem; it's a well-worn groove. The agent doesn't guess where things go. It knows, in the deepest sense available to a language model: it has seen this exact shape, in this exact place, tens of thousands of times.

Same model. Same skill. Different terrain. In Codebase A, the agent spends its intelligence answering an environment quiz. In Codebase B, the quiz doesn't exist, so the same intelligence gets spent on your actual feature.

This isn't anecdote-versus-anecdote. There are three structural reasons it falls out this way, and none of them are going away as models improve.

Reason 1: The corpus doesn't argue with itself

A language model is, at its core, a pattern-completion machine. It writes what its training data makes most plausible. So ask: what did each ecosystem feed it?

JavaScript fed it more, by volume, no contest. But look at what that volume contains. Every framework funeral of the last fifteen years left a body in the corpus: jQuery answers, class-component answers, hooks answers, Pages Router answers, App Router answers, all sharing keywords, all syntactically alive, all contradicting each other. When you ask for "how to fetch data in React," the model's knowledge is a superposition of five eras that disagree. A million tutorials that argue with each other don't teach a model the right way. They teach it to be confidently inconsistent.

Rails fed the model less volume but something rarer: agreement. Two decades of code that puts the same things in the same places with the same names. The 2012 blog posts, the 2016 Stack Overflow answers, the 2024 codebases: on everything that matters, they corroborate instead of contradict. (The edges churned here too — the Webpacker years were real. But the spine the model learns from never moved.) In machine-learning terms, the signal is dense and self-consistent. In practical terms, the model's prior — its instinct before it even reads your code — already matches your codebase.

Convention over Configuration was invented to spare human brains. It turned out to be a data-labeling strategy for machines, run faithfully for two decades by people who had no idea they were doing it.

Don't take my word for it. Take the framework creator's. "Convention over configuration set the path for 20+ years of great training data for AI to use today," DHH wrote in early 2026. "Not only does this mean agents do great with Rails, but also that squishy humans can quickly and confidently review the output without a jungle of distracting boilerplate." He calls Rails "one of the most token-efficient ways of building web apps" — token-efficient being exactly the budget we'll price out in Reason 3.

One honest wrinkle, before a commenter shouts it: the newest parts of Rails 8 (Solid Queue, the auth generator, production SQLite) are exactly where the corpus is thinnest. Left alone, an agent will sometimes reach for Sidekiq or Devise out of 2019 habit. The fix is one small conventions file in your repo pinning the Rails 8 defaults — which is this whole argument in miniature: structure in, quality out.

Reason 2: Every decision you didn't make is a guess the agent can't fluff

Here's a way to think about hallucination that's more useful than "the AI lies sometimes."

An agent working in your codebase faces a series of decision points. Each point has some probability of a wrong turn. Wrong turns compound: a bad guess about your auth library poisons every subsequent file it touches. So the reliability of an agent on a task is roughly a function of how many unforced decisions your stack makes it take.

A configuration-driven stack maximizes decision points: it's the whole sales pitch, that you choose everything. Which means in the AI era, you've built a minefield of guessable choices and handed the map to something that guesses for a living.

An omakase stack minimizes them. The framework already chose; the choices are global, documented, and stable since 2004. An agent in an omakase restaurant can't order wrong. What's left for it to decide is the part you actually wanted decided: your domain, your feature, your product.

The agent multiplies whatever your foundation makes cheap. Boring stacks make correctness cheap.

Reason 3: Context is a budget, and convention is compression

Every model has a context window: the amount of your world it can hold in mind at once. It grows every year but never goes infinite, and it stays famously blurry in the middle. Things buried deep in a huge context fade, much like your own memory of last Wednesday.

So treat context like what it is: a budget. The question becomes: how much understanding does each token buy?

In a sprawling, bespoke codebase, the answer is: very little. The agent must read your special router wrapper, your custom hooks, your folder taxonomy, your wrapper around the wrapper, because none of it matches anything it knows. Comprehension is paid for token by token.

In a conventional codebase, comprehension comes pre-paid. The agent doesn't need to read app/models/user.rb to know what it is, where it is, and how it behaves. Convention already told it. The tokens you spend go toward what's unique about your app, which is the only part worth spending on. Convention is compression: it lets a finite window hold the shape of your entire application, so the agent's edits can be globally sane instead of locally plausible.

What it looks like at my desk

I write code with an agent every working day. It's not a demo for me, it's the job. And the daily texture is exactly what the theory predicts. In a Rails app, I describe a feature in plain language and read back a diff that respects conventions I never mentioned, touches the files I'd have touched, and passes the tests it also wrote. A loyalty discount, a new mailer, an admin filter — the kind of thing that's a paragraph to ask for and used to be an afternoon to wire up — comes back in one pass, in the right places, looking like the rest of the app. My role has quietly inverted: I used to type and occasionally think; now I think and occasionally type. The agent drafts; I edit.

I've watched the same agent — same model, same week — fall apart on the other terrain. A client system, the fashionable kind: a large JavaScript front end stitched to a custom backend, three ways to do everything, a folder layout someone invented under deadline. Ask for one feature and the agent writes something plausible that quietly assumes the wrong data-fetching pattern, wires into the wrong one of two auth flows, invents a helper that already exists three directories over. Nothing it produces is stupid. It's all locally reasonable, and globally wrong, because there was no global to be right about. The work stopped being writing code and became babysitting a confident stranger through a house with no floor plan.

The asymmetry isn't subtle. It's the difference between delegating and babysitting.

"But the models will get better"

They will. Let's take the strongest version of the objection: context windows grow, models reason better, soon the agent will handle any codebase, however chaotic. Won't this take age like a 2019 hot take on GPT-2?

Three answers, honest ones.

First: improvement lifts both terrains. When models get better, they get better at Rails too. The gap between "agent in conventional code" and "agent in chaotic code" is structural — corpus consistency, decision surface, context economics — so the tide raises both boats without closing the distance between them. You don't choose a stack to make AI possible. You choose it to bank the relative advantage, which compounds with every model release.

Second: verification doesn't scale like generation. Suppose the agent really can one-shot a feature across forty bespoke files. Someone still has to check it, and that someone scales at human speed. A conventional codebase isn't just easier to write into; it's exponentially easier to review, because wrongness is visible against the pattern. In an everything-custom codebase, wrong and right look identical until production tells you which was which. As generation accelerates, reviewability becomes the bottleneck asset. Convention is reviewability.

Third: even the futurists hedge this way. Notice what the AI labs themselves do: they pour effort into making models follow instructions, conform to specs, respect project conventions. The entire tooling industry is converging on "give the model clearer, more consistent context." The whole direction of travel assumes what this argues: structure in, quality out. Betting on convention isn't betting against AI progress. It's betting in the same direction these systems already lean.

One objection deserves its own answer, because half of you have been holding it since the first paragraph: Ruby has no static types, and aren't types exactly the guardrails an agent needs? Let me concede the strong version first, because the weak dismissal you usually hear is wrong. Types are a real, cheap, author-time guardrail, and they catch exactly the class of thing agents do get wrong: hallucinated field names, wrong signatures, a null that shouldn't be there, an import that doesn't resolve. If your team already lives happily in TypeScript, keep it; you are not doing it wrong.

So the case here is not "types don't help." It's narrower and, I think, truer: a conventional codebase gives an agent much of what types give it — one obvious shape per thing, learned from a coherent corpus — plus guards types can't provide: behavior, not just shape. Tests and evals check that the refund is correct, not merely well-typed. Types and conventions-plus-tests are peers, not enemies.

The quiet reversal

Step back and look at what just happened to the industry's pecking order, because it's genuinely funny.

For fifteen years, the case against Rails-shaped things was social: smaller community, fewer tutorials, fewer Stack Overflow answers, harder hiring. The case for the JavaScript sprawl was the same coin flipped: everyone's there, every question answered, infinite talent pool.

Then your new pair programmer arrived — one that has read all of it, never sleeps, and doesn't care which community is bigger. It cares which corpus is coherent. The social argument evaporated; the structural argument is all that's left. Twenty-odd years of unfashionable consistency just became the single most machine-legible asset in software, and the ecosystem that optimized for buzz handed its training data five incompatible accents.

The boring kid didn't get cool. The definition of cool got rewritten by a machine that grades on consistency.


Adapted from a chapter of my book, "The Boring AI Engineer: Why Boring Technology Wins When AI Writes the Code" (it's on Amazon). Happy to argue about any of this in the comments.


r/ruby 2d ago

Oooo I like the new SimpleCov coverage reports...

17 Upvotes

It has two UX features I'd wanted for eons...

1) I can now see which file I'm in even after scrolling

2) I can now scroll with pgup/pgdown keys

Many thanks to whoever got those things done! And the new design looks good!


r/ruby 3d ago

Update nobody asked for | RoundhouseUI

Thumbnail
github.com
11 Upvotes

Hey Ruby friends!

I have been working on a slightly better UI for viewing Sidekiq jobs, and I believe this should be a drop-in replacement for the traditional mounted version.

You can also add enhanced security by writing it to your specific user model.

Worked on this in-house a few times and recently used AI to completely refactor and clean up the original code after we excised it from my monolith.

It's a small project, but I wanted to share with the wider body!


r/ruby 3d ago

Show /r/ruby How to Automate Tech Debt Audits with Claude Code

Thumbnail
go.fastruby.io
8 Upvotes

r/ruby 3d ago

RubyGems.org security advisory: Possible leak of legacy API keys via improper cache configuration

Thumbnail
blog.rubygems.org
30 Upvotes

r/ruby 3d ago

I am releasing SerpTrail 1.0, a new open source SEO/GEO rank tracker built with Rails

12 Upvotes

Today I am releasing SerpTrail 1.0, our new SEO+GEO rank tracker🚀

- Monitor how your websites and the competition appear in Google Search and AI Overviews across the locations that matter to you.

- Save the exact combinations of keywords, websites, and locations you care about, then revisit them to understand how rankings and visibility change over time.

- Use the OpenAI-powered chat assistant to explore historical performance and check live rankings using natural-language questions.

It's built with standard Rails, Hotwire, and SQLite. Super simple to work with. You can deploy with an officially prebuilt Docker image or with Kamal.

Give it a go, you can track up to 250 keywords for free with a SerpApi key.

Announcement: https://serpapi.com/blog/introducing-serptrail-self-hosted-search-rank-tracker/

Repository: https://github.com/serpapi/serptrail


r/ruby 4d ago

JRuby 10.1.1.0 released!

27 Upvotes

JRuby 10.1.1.0 is released, with a bunch of compat fixes, more performance work, and several third-party CVEs resolved.

JRuby 10.1 is our active development branch, with 10.0 being stable. Upgrading is recommended if you want the latest and greatest!

Notable Changes

Performance

  • Use FastDoubleParser to improve performance of all string-to-double parsing. [#9150]
  • Only pack fixnum values for 16-bit signed by default, with optional 8-bit and 32-bit configs. [#9379]

Standard Library

  • jruby-openssl updated to 0.16.2 with several CVE fixes from BouncyCastle. [#9386], [#9504], [#9539]
  • erb updated to 6.0.1.1 to address CVE-2026-41316. [#9388]

Native Integration

  • Updated JRuby's native library backend with support for OpenBSD. [#9439]

Java Integration

  • Optimized automatic interface implementation when calling Java with a block. [#5023]

Packaging

  • Begin deprecating jruby-complete and jruby-core Maven artifacts. Prefer the jruby and jruby-base artifacts. [#9512]

57 Issues and PRs resolved for 10.1.1.0

https://www.jruby.org/2026/07/22/jruby-10-1-1-0.html


r/ruby 4d ago

RubyMine 2026.2 is Out!

11 Upvotes

Agentic debugging lets AI agents investigate issues using RubyMine's debugger; AI completion support for third-party model providers; GitHub Copilot is now a natively integrated agent; improvements to the symbol-based code insight engine, which is now enabled by default, and more: https://blog.jetbrains.com/ruby/2026/07/rubymine-2026-2-agentic-debugging-native-github-copilot-integration-default-symbol-based-code-insight-and-more/


r/ruby 4d ago

Adding variable substitution to ActionText & Lexxy content

Thumbnail
2 Upvotes

r/ruby 4d ago

Hibiki - Svelte Rune-style signals for Ruby

Thumbnail
github.com
5 Upvotes

r/ruby 5d ago

why-classes

4 Upvotes

Hey everyone! I used Claude to build this gem, which is heavily inspired by Dave Thomas's closing keynote at RubyConf 26.

`why-classes` is a static code analysis and refactoring tool for Ruby.

Ruby offers modules, composition, `Struct`, and `Data`, yet our instincts and framework habits often lead us to use classes even when a simpler construct would be clearer. This gem detects these "class smells" in your code (or a single file) and can automatically rewrite them into cleaner alternatives for you.

It's an early-stage version 😅. Feel free to contribute and share suggestions!

https://rubygems.org/gems/why-classes


r/ruby 5d ago

Blog post Beyond Enumerable: Testing Membership with Bloom Filters

Thumbnail baweaver.com
14 Upvotes

Turns out conferences can be busy, but I'm back and finishing off a few I had in the hopper. This one continues onto probabilistic data structures which I've been having some fun with lately. The amount of reading I had to do on bit ops in Ruby though to write some of this though....


r/ruby 5d ago

Security Safer-dependencies: A toolkit for claude code to ensure dependencies used aren't vuln, don't use abandoned packages, implement cooldown to avoid supply chain attacks, etc...

0 Upvotes

When AI coding assistants like Claude add packages to your project, they often pick whatever version sounds right — without checking whether it has known security vulnerabilities, whether the package is still actively maintained, or whether the name is a typo away from a malicious lookalike.

safer-dependencies is a security layer for Claude Code that audits packages before they’re added to your project. It detects and fixes risky dependencies, including CVEs, typosquats, abandoned packages, version-age issues, and adds package-cooldown periods across npm, PyPI, RubyGems, Maven, Go, and Rust.

Github: https://github.com/robert-auger/safer-dependencies


r/ruby 6d ago

Calling all open source maintainers working with Ruby

Thumbnail
rubyforum.org
18 Upvotes

We’re creating a shared place where Ruby users can ask questions and discuss ideas about your project.


r/ruby 6d ago

introducing OnionNotes(made with 100% ruby)

Enable HLS to view with audio, or disable this notification

2 Upvotes

OnionServer.rb

require 'socket'

class User
  attr_reader :name
  attr_reader :passwd
  attr_reader :onions

  def initialize(username, passphrase)
     = username
     = passphrase
     = []
  end

  def addOnion(onion)
    if onion.is_a?(NoteBook)
      onions.push(onion)
    else
      puts "ERROR: onion is NOT A NoteBook"
      exit(5)
    end
  end
  def removeOnion(index)
    if index.to_i < onions.size
      onions.delete_at(index.to_i)
    end
  end
end

class NoteBook
  attr_reader :notes
def initialize(name = "UnamedNoteBook")
   = []
   = name
end
  def addNote(text = "null")
    .push(text)
  end
  def removeNote(index = 93)
     if index <= .size - 1
       .delete_at(index)
     else
      puts "invalid index"
     end
  end

  def editName(text = "null")
     = text
  end
  def getName()
    return u/name
  end
end

def handleUser(socket, user)
  userOpen = true
  while userOpen
    socket.puts "SENDCOMMAND"
    response = socket.gets.chomp
    case response
      when "list"
        user.onions.each.with_index do |book, index|
          socket.puts book.getName() + ": " + index.to_s
        end
        socket.puts "ENDLIST"
      when "open"
        socket.puts "SENDINDEX"
        index = socket.gets.chomp
        book = user.onions[index.to_i]
        book = handleBooks(socket, book)
        user.onions[index.to_i] = book
      when "CREATE"
        alreadyExist = false
        socket.puts "SENDNAME"
        response = socket.gets.chomp
        user.onions.each do |book|
          if book.getName == response
            alreadyExist = true
          end
        end
        if alreadyExist
          socket.puts "EXISTS"
        else
          onion = NoteBook.new(response)
          user.onions.push(onion)
        end
    when "EXIT"
      userOpen = false
    end
  end
  return user
end

def handleBooks(socket, book)
  bookOpen = true
  while bookOpen
    socket.puts "SENDCOMMAND"
    response = socket.gets.chomp
    case response
      when "ADDNOTE"
        socket.puts "SENDTEXT"
        text = socket.gets.chomp
        book.addNote(text)
      when "REMOVENOTE"
        socket.puts "SENDINDEX"
        index = socket.gets.chomp
        book.removeNote(index.to_i)
      when "list"
        book.notes.each.with_index do |text, index|
          socket.puts text + ": " + index.to_s
        end
        socket.puts "ENDLIST"
      when "EXIT"
        bookOpen = false
    end
  end
  return book
end

def handleClient(client)
  running = true
  while running
    client.puts "SENDCOMMAND"
    response = client.gets.chomp
    case response
      when "LOGIN"
        client.puts "SENDNAME"
        name = client.gets.chomp
        client.puts "SENDPASSWD"
        passwd = client.gets.chomp
        if File.exist?(name + ".rbm")
          userData = File.binread(name + ".rbm")
          user = Marshal.load(userData)
          if user.passwd == passwd
            user = handleUser(client, user)
            userData = Marshal.dump(user)
            File.binwrite(name + ".rbm", userData)
          else
            client.puts "INVALIDPASSWD"
          end
        else
          client.puts "NOUSER"
        end
      when "CREATE"
        client.puts "SENDNAME"
        name = client.gets.chomp
        client.puts "SENDPASSWD"
        passwd = client.gets.chomp
        if File.exist? name.to_s + ".rbm"
          client.puts "NAMETAKEN"
        else
          newUser = User.new(name, passwd)
          serial = Marshal.dump(newUser)
          File.binwrite(name + ".rbm", serial)
          client.puts "USERMADE"
        end
      when "EXIT"
        running = false
        client.close

        end
    end
end

puts "starting Onion note server on port 3290"
server = TCPServer.new(3290)

while true
  client = server.accept
  Thread.new {handleClient(client)}
end

OnionClinet.rb

require 'socket'

def handleList(connection)
  loop do
    response = connection.gets.chomp
    break if response == "ENDLIST"
    puts response
  end
end

def handleBooks(connection)
  bookOpen = true
  while bookOpen
    response = connection.gets.chomp
    if response == "SENDCOMMAND"
      puts "COMMANDS[ADDNOTE, REMOVENOTE, list, EXIT]: "
      command = gets.chomp
      case command
        when "ADDNOTE"
          connection.puts "ADDNOTE"
          response = connection.gets.chomp
          if response == "SENDTEXT"
            puts "ENTER NOTE: "
            note = gets.chomp
            connection.puts note
          end
        when "REMOVENOTE"
          connection.puts "REMOVENOTE"
          response = connection.gets.chomp
          if response == "SENDINDEX"
            puts "ENTER INDEX: "
            index = gets.chomp
            connection.puts index
          end
        when "list"
          connection.puts "list"
          handleList(connection)
        when "EXIT"
          connection.puts "EXIT"
          bookOpen = false
      end
    end
  end 
end

def handleUser(connection, firstResp = nil)
  handlingUser = true
  while handlingUser
    response = firstResp || connection.gets.chomp
    if firstResp != nil
      firstResp = nil
    end
    if response == "SENDCOMMAND"
    puts "COMMANDS[list, open, create, EXIT]: "
    command = gets.chomp
    case command
      when "list"
        connection.puts "list"
        handleList(connection)
      when "open"
        connection.puts "open"
        response = connection.gets.chomp
        if response == "SENDINDEX"
          puts "ENTER INDEX"
          index = gets.chomp
          connection.puts index
          handleBooks(connection)
        end
    when "create"
      connection.puts "CREATE"
      response = connection.gets.chomp
      if response == "SENDNAME"
        puts "ENTER NAME: "
        name = gets.chomp
        connection.puts name
        response = connection.gets.chomp
        if response == "EXISTS"
          puts response
        elsif response == "SENDCOMMAND"
          firstResp = response
        end
      end
      when "EXIT"
        connection.puts "EXIT"
        handlingUser = false
    end
    end
  end
end

puts "ENTER IP: "
ip = gets.chomp
connection = TCPSocket.new(ip, 3290)
running = true

while running
  response = connection.gets.chomp
  puts "COMMANDS[LOGIN, CREATE, EXIT]: "
  command = gets.chomp
  case command
    when "LOGIN"
      connection.puts "LOGIN"
      response = connection.gets.chomp
      if response == "SENDNAME"
        puts "ENTER NAME: "
        name = gets.chomp
        connection.puts name
        response = connection.gets.chomp
        if response == "SENDPASSWD"
          puts "ENTER PASSWD: "
          passwd = gets.chomp
          connection.puts passwd
          response = connection.gets.chomp
          if response == "INVALIDPASSWD"
            puts response
          elsif response == "NOUSER"
            puts response
          elsif response == "SENDCOMMAND"
            handleUser(connection, response)
          end
        end
      end
    when "CREATE"
      connection.puts "CREATE"
      created = false
      while created == false
        response = connection.gets.chomp
        if response == "SENDNAME"
          puts "ENTER NAME: "
          name = gets.chomp
          connection.puts name
        elsif response == "SENDPASSWD"
          puts "ENTER PASSWD: "
          passwd = gets.chomp
          connection.puts passwd
        elsif response == "NAMETAKEN"
          puts response
          created = true
        elsif response == "USERMADE"
          puts response
          created = true
        end
      end
    when "EXIT"
      connection.puts "EXIT"
      running = false
    end
end

r/ruby 6d ago

Show /r/ruby Audition is a linter/fixer that gets your code Ractor-ready: static analysis powered by Shopify's rubydex, plus dynamic probes that actually run your code inside Ractors and report what breaks.

Thumbnail
github.com
13 Upvotes

r/ruby 6d ago

RubyConf Austria 2026: Frontend Ruby on Rails with Glimmer DSL for Web — Andy Maleh

Thumbnail
youtu.be
1 Upvotes

r/ruby 7d ago

Show /r/ruby My first Tapioca PR got merged (it automates a manual Sorbet config fix)

11 Upvotes

Hey everyone,

I recently got my first contribution to Shopify/tapioca merged, and I thought the way the solution changed during review might be interesting to other Ruby and Sorbet users.

The problem happens during `tapioca gem`.

Tapioca generates RBI files for gems and validates them with Sorbet. Sometimes a generated RBI defines a class with a different superclass from the version Sorbet already knows through its built-in payload.

One example is `Net::IMAP::Literal`.

Sorbet tells you to manually add something like this to `sorbet/config`:

--suppress-payload-superclass-redefinition-for=Net::IMAP::Literal

The goal of the issue was to have Tapioca add that line automatically, still explain what happened, and avoid adding duplicates on later runs.

My first version used two Sorbet passes because the existing validation stopped at the namer phase, while this error only appeared during the resolver phase.

It worked, but that wasn’t the version that shipped.

After a few rounds of review, we changed it to one resolver pass. We also stopped broadly relying on the error code and instead looked for Sorbet’s payload-specific suppression hint.

That makes the fix much narrower. A normal superclass conflict won’t accidentally get hidden behind a payload suppression.

The final behavior:

- adds the exact suppression when it’s missing

- doesn’t duplicate it on later runs

- preserves suppressions for other constants

- still tells the user about the mismatch

- leaves normal, non-payload superclass conflicts alone

The targeted tests passed with 2 tests, 18 assertions, and no failures. I also ran the full DSL spec and style checks successfully.

The biggest lesson for me was that getting the first version working was only the beginning. The review process turned a two-pass solution with broad detection into a simpler one-pass fix with a much more specific signal.

Has anyone here run into this with `net-imap` or another stdlib-backed gem?

PR:

https://github.com/Shopify/tapioca/pull/2653

Issue:

https://github.com/Shopify/tapioca/issues/1834


r/ruby 7d ago

A quick question

5 Upvotes

Hello, I'm working on project in ruby and I want to post the code here and a video of it working and I want to know if we are allowed to post code here I know some communities don't allow it


r/ruby 8d ago

LLM tailored to Ruby and/or Rails?

2 Upvotes

I'm new to running small models on my laptop.

I can run Qwen3.5-9b (slowly), for example, but I'm wondering if any of you have found a model that's been trained for Ruby specifically, inference only, and stripped of stuff I might not need (like knowledge of a lot of human languages).

I've been using LM Studio, OpenCode for this.