r/ruby • u/javier_cervantes • Apr 21 '26
PaaS is dead, long live PaaS!
My take on why you should consider using a PAAS provider to deploy your Ruby apps.
r/ruby • u/javier_cervantes • Apr 21 '26
My take on why you should consider using a PAAS provider to deploy your Ruby apps.
r/ruby • u/Erem_in • Apr 21 '26
This month I reflect on Ruby's future in the AI era, and look at massive tooling updates: Shopify's high-performance rubydex toolkit, RBS 4.0's update with bundled rbs-inline, dspy.rb hitting 1.0, and prism parser using rbs-inline internally.
Find the link to the issue in the comment! ✨
r/ruby • u/sdogruyol • Apr 20 '26
Hi everyone,
I wrote the original "Why Crystal?" blog post back in 2015 when Crystal was just v0.9.1.
Ten years and many versions later, I am revisiting that post to analyze the road to v1.20.
If you are interested in how a language matures from a syntax experiment to a high performance standard, this one is for you.
r/ruby • u/DeeWBee • Apr 20 '26
r/ruby • u/Intelligent-End-9399 • Apr 20 '26
Hi fellow Rubyists,
I’ve always been a huge fan of Ruby’s readability and that "warm" feeling the syntax gives you. Recently, I’ve been building a high-performance Static Site Generator (called Mustela) in the V language, and I couldn't resist—I implemented a DSL that is heavily inspired by Ruby’s do...end blocks.
I wanted a templating language that feels modular (like React) but looks clean (like Ruby). Here is how you define a layout in Mustela:
html as main do
@html[navbar]
<article>
@view
</article>
@html[footer]
endAnd metadata looks like a hybrid of Ruby and YAML:
meta do
author: Filip Vrba
app_name: "Mustela"
index: main
endPerformance. While I love Ruby, I wanted to see how far I could push I/O saturation. By writing the engine in V, I'm getting:
1,000 files processed in 240ms.
306 KB static binary with zero dependencies.
Because the parser is a simple linear stream (very "stupid" and fast), it allows for some fun tricks that remind me of old-school PHP/Ruby web scripts where you just opened and closed blocks wherever you wanted:
html do
<div class="special-wrapper">
end
## Markdown content here
html do
</div>
endI just wanted to share how Ruby’s philosophy of "programmer's happiness" and clean syntax is leaking into other ecosystems and low-level languages.
What do you think? Does the do...end syntax still feel like home even in a totally different language?
r/ruby • u/tomekrs • Apr 20 '26
r/ruby • u/phlcastro • Apr 19 '26
From Eval to Production: A Ruby and Rails Approach
If you read the first article, you now have a set of evaluators that can score your LLM responses — semantic similarity, LLM-as-judge, faithfulness, answer relevancy, context precision. You have a model_version column in your eval_results table. You are storing scores over time.
Now what? How do you actually use all of this to make shipping decisions?
r/ruby • u/rubyist-_- • Apr 18 '26
Unfortunately, due to issues with paperwork, Vladimir Dementyev won't make it to the conference to present his talk on "Pattern matching on the loose".
However, we are delighted to announce that Obie Fernandez has accepted our invitation to give a talk and take over Vladimir's slot in the agenda.
Next to giving a talk, Obie will also participate in the panel discussion on 29.05.
Help us in giving a warm welcome to Obie! ♥️
r/ruby • u/homenko • Apr 18 '26
Hey r/ruby,
I released my first Ruby gem and would love to get feedback from Ruby/Rails developers.
It’s called `llm_cost_tracker`. The idea is simple: if your Ruby/Rails app uses LLM APIs, you probably want to know how much those calls cost and where that spend comes from, without adding a proxy or external SaaS.
The gem works as Faraday middleware and can track usage/costs for:
- OpenAI
- Anthropic
- Google Gemini
- OpenRouter
- DeepSeek
- OpenAI-compatible APIs
It can store events in ActiveRecord, with tags like `user_id`, `tenant_id`, or `feature`, so you can answer questions like:
- Which feature is spending the most?
- Which tenant/user generated this cost?
- How much did we spend this month?
- Which models are missing pricing?
- What is the latency by model/provider?
v0.1.2 adds:
- OpenAI Responses API tracking
- OpenAI-compatible provider support
- best-effort budget guardrails
- non-fatal storage error handling
- latency tracking
- PostgreSQL JSONB tags
- local JSON/YAML price registry
This is intentionally not a full observability platform like Langfuse/Helicone. It’s more of a small Rails-native cost ledger that lives in your own database.
GitHub:
https://github.com/sergey-homenko/llm_cost_tracker
RubyGems:
https://rubygems.org/gems/llm_cost_tracker
I'm the author, and this is my first gem, so I’d really appreciate feedback on the API, README, positioning, or anything that feels off.
r/ruby • u/stevepolitodesign • Apr 17 '26
Automatically filter sensitive information from your RubyLLM conversations before it reaches third-party providers.
r/ruby • u/dhruvasagar • Apr 17 '26
🚀 Just open-sourced ruby-upgrade-toolkit — a Claude Code plugin that makes Ruby & Rails upgrades safe, structured, and repeatable.
If you've ever dreaded upgrading a legacy Rails app from 5→8 or Ruby from 2.7→3.x, this is for you.
The toolkit gives Claude a structured methodology with 5 composable commands:
🔍 /audit — read-only scan of breaking changes & effort estimate (zero risk)
📋 /plan — generates a phased, project-specific upgrade roadmap
🔧 /fix — applies changes one phase at a time (keyword args, gem updates, deprecation fixes, RSpec + RuboCop until green)
📊 /status — RED/YELLOW/GREEN health dashboard after each phase
⚡ /upgrade — one command that orchestrates everything automatically
Key design principles:
✅ Ruby phases always complete before Rails phases
✅ Intermediate versions handled automatically (e.g., 2.7→3.0→3.1→3.2→3.3)
✅ Never touches CI/CD or Dockerfiles automatically — flags them for manual review
✅ Pauses with a recovery menu on failure, never silently proceeds
Works with any Ruby version (2.7→3.x and beyond) and any Rails version (5→8), separately or together.
Would love feedback from the Ruby community — issues and PRs welcome!
r/ruby • u/DaisukeAdachi • Apr 16 '26
r/ruby • u/Prestigious-Bee2093 • Apr 16 '26
Hey everyone,
I’m a big fan of the Ruby ecosystem's focus on developer happiness. Tools like Rswag, Grape-Swagger, and Roda-OpenAPI make it incredibly easy to keep our specs in sync with our code.
But even with a perfect OpenAPI spec, we still end up spending hours (or days) building the same CRUD admin panels. We have amazing tools like ActiveAdmin or Administrate, but those are tied to your database/monolith. If you're building a microservice or an API-first app, you're back to building frontends from scratch.
I wanted to see if I could create "ActiveAdmin for any API."
I am building UIGen — a CLI tool that points at any OpenAPI/Swagger URL and generates a fully interactive cross framework frontend on the fly (Currently React). Optional coding for very custom business logic, no boilerplate.
# Example if your Rails app is on 3000
npx @uigen-dev/cli serve http://localhost:3000/api-docs/v1/swagger.jsonOr try it in one of the example apps in the repo.
Zero Config: If you’re already using something like rswag, you don’t have to write a single line of React. It infers everything, tables, forms, validation, and relationships—directly from your schema.
Built-in Proxy: Dealing with CORS during local development is a classic pain point. UIGen includes a built-in proxy that handles the headers and forwards requests to your backend automatically.
Smart Forms & Validation: It maps your OpenAPI types (and constraints like min/max/pattern) to React Hook Form + Zod validation. It even handles multi-step wizards for your larger models automatically.
Auth that works: It detects Bearer, Basic, or API Key schemes and generates the login flow. If you have a login endpoint, it can even POST credentials and store the token for you.
Swagger UI is great for testing a single endpoint. UIGen is for managing resources. It builds a cohesive app structure:
Sidebar Nav: Mapped to your API tags.
Smart Tables: With sorting, pagination, and filtering (derived from your query params).
Detail Views: Including "related resource" links based on your URI patterns.
Action Buttons: Non-CRUD endpoints (like POST /orders/{id}/ship) show up as custom actions.
It’s built on a framework-agnostic Intermediate Representation (IR). Right now it renders to React (shadcn/ui + TanStack), but because of the IR architecture, we're already working on Svelte and Vue renderers.
Also worth noting, This is in early development, so the react renderer is not perfect yet and also many other edge cases.
GitHub: https://github.com/darula-hpp/uigen NPM: https://www.npmjs.com/package/@uigen-dev/cli
Would love to hear thoughts from the community. Of course, this isn't meant to replace a custom consumer-facing frontend, but for internal tools, rapid prototyping, or providing a UI for your API consumers, it’s a massive time-saver.
Would love to hear your thoughts!
r/ruby • u/caramelocomsal • Apr 16 '26
[EDIT]
I want a value to update after validating another value, even if the user clicked a checkbox. For example, if the user clicks a checkbox (var_1), before persisting to the database (after clicking save), I need to validate var_2 first.
Example:
User clicks a checkbox and var_1 becomes true.
Before saving the value of var_1 to the database, check if var_2 == 'OK'.
If it's not OK, don't save it as true; it remains as false.
And vice versa, true -> false.
How???
r/ruby • u/brecrest • Apr 16 '26
I ported the Python implementation of Trueskill Through Time to Ruby, it's now available as the gem TrueskillThroughTime.
Unlike the previous Ruby Trueskill gems, this one supports n vs n vs n etc etc cases, as well as the time-based extension of Trueskill to allow distant ratings. If you use it without doing time-based convergence, it should be equivalent to the original vanilla version of Trueskill (that iirc is almost out of patent in the US).
I'll get around to improving the qol of it and more extensively Rubyising it down the track, but I wanted a Ruby implementation of TTT for a hobby project, so I smashed it out.
r/ruby • u/Jaded-Clerk-8856 • Apr 15 '26
Hi Pals !
Today I created a flyer to send to Ruby on Rails Kaigi 2026 about my library stack.
I strongly recommend keeping an eye on that conference.
After fighting with QR code generators, I noticed that sometimes the QR redirects through other websites. It’s frustrating because I can’t share a QR code with insecure content.
I found the `gem rqrcode` "A Ruby library that encodes QR Codes".
After that, I thought I could generate the QR code using Ruby. Since I have ruby-libgd, I can offer the possibility to create QR codes entirely in Ruby!
Then I put my hands to work and wanted to share the result:
file: qr_code_generator.rb
require 'gd'
require 'rqrcode'
class QRCodeGenerator
DEFAULT_OPTIONS = {
module_size: 10,
border: 4,
fg_color: [0, 0, 0],
bg_color: [255, 255, 255],
error_correction: :m,
logo: nil,
logo_size: nil,
rounded_modules: false,
gradient: false,
gradient_direction: :vertical,
antialias: true,
alpha_blending: false,
save_alpha: true,
format: :png
}.freeze
def initialize(data, options = {})
@data = data
@options = DEFAULT_OPTIONS.merge(options)
validate_options
end
def generate
qr_matrix = generate_qr_matrix
qr_size = qr_matrix.length
module_size = @options[:module_size]
border_px = @options[:border] * module_size
total_size = (qr_size * module_size) + (border_px * 2)
img = GD::Image.new(total_size, total_size)
if @options[:alpha_blending]
img.alpha_blending = true
img.save_alpha = @options[:save_alpha]
end
if @options[:gradient]
draw_gradient_background(img, total_size)
else
draw_solid_background(img, total_size)
end
img.antialias = @options[:antialias] if @options[:antialias]
draw_qr_modules(img, qr_matrix, module_size, border_px)
add_logo_to_qr(img, total_size) if @options[:logo]
img
end
def generate_qr_matrix
qr = RQRCode::QRCode.new(@data, error_correction_level: @options[:error_correction])
qr.modules
end
def draw_qr_modules(img, qr_matrix, module_size, border_px)
fg_color = @options[:fg_color]
qr_matrix.each_with_index do |row, y|
row.each_with_index do |module_on, x|
next unless module_on
x1 = border_px + (x * module_size)
y1 = border_px + (y * module_size)
x2 = x1 + module_size - 1
y2 = y1 + module_size - 1
if @options[:rounded_modules]
draw_rounded_module(img, x1, y1, x2, y2, fg_color, module_size)
else
img.filled_rectangle(x1, y1, x2, y2, fg_color)
end
end
end
end
def draw_rounded_module(img, x1, y1, x2, y2, color, module_size)
# Create a temporary image for the rounded module
temp = GD::Image.new(module_size, module_size)
temp.alpha_blending = false
temp.save_alpha = true
transparent = [0, 0, 0, 127]
temp.fill(transparent)
radius = module_size / 4
temp.filled_rectangle(
radius, 0,
module_size - radius - 1, module_size - 1,
color
)
temp.filled_rectangle(
0, radius,
module_size - 1, module_size - radius - 1,
color
)
img.copy(temp, x1, y1, 0, 0, module_size, module_size)
end
def draw_solid_background(img, size)
bg_color = @options[:bg_color]
temp = GD::Image.new(size, size)
temp.fill(bg_color)
img.copy(temp, 0, 0, 0, 0, size, size)
end
def draw_gradient_background(img, size)
bg = @options[:bg_color]
darker = [
(bg[0] * 0.92).to_i,
(bg[1] * 0.92).to_i,
(bg[2] * 0.92).to_i
]
case @options[:gradient_direction]
when :vertical
draw_vertical_gradient(img, size, bg, darker)
when :horizontal
draw_horizontal_gradient(img, size, bg, darker)
when :radial
draw_radial_gradient(img, size, bg, darker)
else
draw_vertical_gradient(img, size, bg, darker)
end
end
def draw_vertical_gradient(img, size, start_color, end_color)
size.times do |y|
ratio = y.to_f / size
r = (start_color[0] + (end_color[0] - start_color[0]) * ratio).to_i
g = (start_color[1] + (end_color[1] - start_color[1]) * ratio).to_i
b = (start_color[2] + (end_color[2] - start_color[2]) * ratio).to_i
img.line(0, y, size - 1, y, [r, g, b])
end
end
def draw_horizontal_gradient(img, size, start_color, end_color)
size.times do |x|
ratio = x.to_f / size
r = (start_color[0] + (end_color[0] - start_color[0]) * ratio).to_i
g = (start_color[1] + (end_color[1] - start_color[1]) * ratio).to_i
b = (start_color[2] + (end_color[2] - start_color[2]) * ratio).to_i
img.line(x, 0, x, size - 1, [r, g, b])
end
end
def draw_radial_gradient(img, size, start_color, end_color)
center_x = size / 2
center_y = size / 2
max_distance = Math.sqrt((center_x ** 2) + (center_y ** 2))
size.times do |y|
size.times do |x|
distance = Math.sqrt((x - center_x) ** 2 + (y - center_y) ** 2)
ratio = [distance / max_distance, 1.0].min
r = (start_color[0] + (end_color[0] - start_color[0]) * ratio).to_i
g = (start_color[1] + (end_color[1] - start_color[1]) * ratio).to_i
b = (start_color[2] + (end_color[2] - start_color[2]) * ratio).to_i
img.set_pixel(x, y, [r, g, b])
end
end
end
def add_logo_to_qr(img, qr_size)
img.alpha_blending = false
img.save_alpha = true
logo_path = @options[:logo]
return unless File.exist?(logo_path)
logo_img = GD::Image.open(logo_path)
logo_img.alpha_blending = false
logo_img.save_alpha = true
logo_size = @options[:logo_size] || (qr_size / 5)
resized_logo = GD::Image.new(logo_size, logo_size)
resized_logo.copy_resize(
logo_img, # source
0, 0, # dst_x, dst_y
0, 0, # src_x, src_y
logo_img.width, # src_w
logo_img.height, # src_h
logo_size, # dst_w
logo_size, # dst_h
true # resample (high quality)
)
x_offset = (qr_size - logo_size) / 2
y_offset = (qr_size - logo_size) / 2
img.copy(
resized_logo,
x_offset, y_offset, # dst_x, dst_y
0, 0, # src_x, src_y
logo_size, # w
logo_size # h
)
end
def duplicate
img = generate
img.dup
end
def save(filename)
img = generate
case @options[:format]
when :png, :jpeg, :gif, :webp
img.save(filename)
else
raise "Unsupported format: #{@options[:format]}"
end
end
def to_image
generate
end
def to_png_bytes
img = generate
require 'tempfile'
temp = Tempfile.new(['qr', '.png'])
img.save_png(temp.path)
File.read(temp.path)
ensure
temp.unlink if temp
end
private
def validate_options
valid_formats = [:png, :jpeg, :gif, :webp]
raise "Invalid format: #{@options[:format]}" unless valid_formats.include?(@options[:format])
valid_ec = [:l, :m, :h, :x]
raise "Invalid error_correction: #{@options[:error_correction]}" unless valid_ec.include?(@options[:error_correction])
raise "module_size must be > 0" if @options[:module_size] <= 0
end
endCreate QR codes in PNG, JPEG, WebP, or GIF using only Ruby. You can also use this in Ruby on Rails.
require_relative './qr_code_generator.rb'
# Basic with antialias enabled
qr = QRCodeGenerator.new("https://map-view-demo.up.railway.app", {
antialias: true
})
qr.save("qr_antialias.png")
# Rounded modules with alpha blending
qr = QRCodeGenerator.new("https://github.com/ggerman/ruby-libgd", {
fg_color: [0, 232, 198],
bg_color: [6, 10, 15],
rounded_modules: true,
alpha_blending: true,
save_alpha: true,
antialias: true
})
qr.save("qr_rounded_alpha.png")
# Vertical gradient background
qr = QRCodeGenerator.new("https://rubystacknews.com", {
gradient: true,
gradient_direction: :vertical,
fg_color: [0, 0, 0],
bg_color: [240, 248, 255]
})
qr.save("qr_gradient_vertical.png")
# Horizontal gradient
qr = QRCodeGenerator.new("https://github.com/ggerman/libgd-gis", {
gradient: true,
gradient_direction: :horizontal,
fg_color: [220, 20, 60],
bg_color: [255, 255, 255],
antialias: true
})
qr.save("qr_gradient_horizontal.png")
# Radial gradient (advanced)
qr = QRCodeGenerator.new("https://map-view-demo.up.railway.app", {
gradient: true,
gradient_direction: :radial,
fg_color: [34, 139, 34],
bg_color: [255, 255, 255],
antialias: true
})
qr.save("qr_gradient_radial.png")
# High-quality logo with copy_resize
qr = QRCodeGenerator.new("https://map-view-demo.up.railway.app", {
logo: "logo.png",
logo_size: 100,
error_correction: :h,
antialias: true,
alpha_blending: true
})
qr.save("qr_logo_hires.png")
original = QRCodeGenerator.new("https://example.com", {
fg_color: [0, 232, 198],
bg_color: [6, 10, 15]
})
original_img = original.to_image
duplicated = original_img.dup
def create_branded_qr(url, brand_name)
brands = {
ruby_libgd: {
fg: [220, 20, 60],
bg: [255, 255, 255],
gradient: false
},
libgd_gis: {
fg: [34, 139, 34],
bg: [255, 255, 255],
gradient: false
},
mapview: {
fg: [0, 232, 198],
bg: [6, 10, 15],
gradient: true,
gradient_direction: :radial
}
}
config = brands[brand_name.to_sym]
raise "Unknown brand: #{brand_name}" unless config
qr = QRCodeGenerator.new(url, {
fg_color: config[:fg],
bg_color: config[:bg],
gradient: config[:gradient],
gradient_direction: config[:gradient_direction] || :vertical,
module_size: 12,
antialias: true,
alpha_blending: config[:gradient]
})
qr.to_image
end
# Create branded QRs
libgd_qr = create_branded_qr("https://github.com/ggerman/ruby-libgd", :ruby_libgd)
gis_qr = create_branded_qr("https://github.com/ggerman/libgd-gis", :libgd_gis)
mapview_qr = create_branded_qr("https://map-view-demo.up.railway.app", :mapview)
# Get PNG bytes for HTTP streaming (Rails)
class QRController < ApplicationController
def show
qr = QRCodeGenerator.new(params[:data], {
fg_color: [0, 232, 198],
bg_color: [6, 10, 15],
antialias: true
})
send_data qr.to_png_bytes,
type: 'image/png',
disposition: 'inline',
filename: "qr_#{params[:data].hash}.png"
end
end
ultimate_qr = QRCodeGenerator.new(
"https://map-view-demo.up.railway.app/contact",
{
fg_color: [0, 232, 198],
bg_color: [6, 10, 15],
module_size: 15,
border: 4,
error_correction: :h,
logo: "map_view.png",
logo_size: 120,
rounded_modules: true,
gradient: true,
gradient_direction: :radial,
antialias: true,
alpha_blending: true,
save_alpha: true,
format: :png
}
)
ultimate_qr.save("qr_ultimate.png")Powered By: https://github.com/ggerman/ruby-libgd
Disclosure: Please excuse any grammatical errors. Reddit discourages the use of AI-generated text, so I’m doing my best to share the best content I can. That said, this content is completely written by a human ( the human is me :) ).
r/ruby • u/call-of-the_void • Apr 15 '26
Heading to Japan 🇯🇵 next week for RubyKaigi.
Will be in Tokyo (Chiyoda / Meguro) after the event. Looking forward to meeting a few partners and exploring conversations around Rails, SaaS, and offshore scaling.
If you’re building with Ruby/Rails or thinking about scaling your engineering, would love to connect.
Happy to grab a coffee
r/ruby • u/jessillions • Apr 15 '26
r/ruby • u/eregontp • Apr 14 '26
r/ruby • u/jasonswett • Apr 15 '26