Ruby On Rails 2026
Introduction
Got new job which wanted this skill to poking around to have another go.
Installation
No robot back in 2020 when last I gave this the initial look. It is telling me to use asdf with a specific version. Clearly I need to check this out before doing. For Ubuntu its self it say
sudo apt install -y build-essential libssl-dev libreadline-dev zlib1g-dev libsqlite3-dev
So went with the version of asdf it said because the second instructions fail because there is no completions directory with the latest release so for asdf
sudo apt install curl git
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.14.0
echo '. "$HOME/.asdf/asdf.sh"' >> ~/.bashrc
echo '. "$HOME/.asdf/completions/asdf.bash"' >> ~/.bashrc
source ~/.bashrc
Now for rails went with 3.3.11
asdf plugin add ruby
asdf install ruby 3.3.11
asdf global ruby 3.3.11
And for Bundler and Rails
gem install bundler -v 2.4.19
gem install rails
rails -v Rails 8.1.3
Gemfile Stuff
Gemfile Basics
The Gemfile is Ruby’s equivalent of npm’s package.json. It defines which gems your application depends on and in which environments they should be installed.
Bundler supports groups, which work similarly to npm’s dependencies and devDependencies.
Grouping Gems
You can group gems so they are only installed or loaded in specific environments.
A common example is the development group:
group :development do
gem "web-console"
gem "rubocop", require: false
gem "rubocop-rails", require: false
gem "htmlbeautifier"
end
You can also combine multiple groups:
group :development, :test do
gem "debug", platforms: %i[mri windows], require: "debug/prelude"
gem "bundler-audit", require: false
end
Gems inside these blocks are only installed when Bundler is run in those environments, and Rails only loads them when running in that environment.
Inline Group Syntax
Bundler also supports an inline form, which is functionally identical to the block syntax:
gem "sorbet", group: :development
This line is equivalent to:
group :development do
gem "sorbet"
end
Use whichever style reads better for your Gemfile.
Gems Loaded in All Environments
Any gem not placed inside a group is installed and loaded in every environment (development, test, production).
For example:
gem "sorbet-runtime"
This gem will be available everywhere because it is not wrapped in a group.
Making my Website Page
Getting Started
So set up and ready to go. A few things to note
- routes in /config/routes
- views in /app/views/<page-name>
- layouts in /app/views/layouts
- components in app and a PageHeader component component with
So I generated an app, page and component
rails new bibble_web_ror --css=tailwind
cd bibble_web_ror
rails generate controller Bill index
rails generate component PageHeader
Told the robot what I did for view and it kindly gave me the rest. Here is the amended layout
<body class="min-h-screen bg-(--color-bg-page) text-(--color-text-primary)">
<a href="#main-content" class="sr-only focus:not-sr-only">Skip to content</a>
<main id="main-content"
class="max-w-300 bg-(--color-bg-sections) px-2 pt-2 shadow-(--shadow-page) not-only:mx-auto">
<%= render PageHeaderComponent.new(title: @title, subtitle: @subtitle) %>
<div class="u-container">
<%= yield %>
</div>
</main>
<footer class="p-4 text-center text-sm text-(--color-text-primary)">
© 2025 Iain Wiseman
</footer>
</body>
And for the component
<header class="mb-6">
<h1 class="text-3xl font-bold text-(--color-text-primary)">
<%= @title %>
</h1>
<% if @subtitle.present? %>
<p class="mt-1 text-(--color-text-secondary)">
<%= @subtitle %>
</p>
<% end %>
</header>
The generate component failed because I am on rails 8. Suspect there a billion of these generators but we chose view_component. You need to add it to the gemfile and do a bundle install.
rails generate view_component:erb PageHeader
Next it failed again because ruby expects there to be a .rb file which is not automatically generated. Not sure why but suspect my new employer will let me know. Anyway here it is.
class PageHeaderComponent < ViewComponent::Base
def initialize(title:, subtitle: nil)
@title = title
@subtitle = subtitle
end
end
The robot says you can generate this with
rails generate component PageHeader --ruby
So this did not work so I did something I hate doing, read the docs which show
rails generate view_component:component PageHeader
And of course this did work.
Tailwind
This did not work either. Ended up following the excellent instructions on their site for tailwind which mainly involved.
bundle add tailwindcss-rails
rails tailwindcss:install
I found I had to run with bin/dev as the generation of color did not happen
bin/dev
The config tailwind.config.js needs to be empty like in vue. And the theme stuff is in app/assets/stylesheets/tailwind/application.css
Debugger
This seemed harder than it needed to be. I think there have been several approaches at it which has left some stuff behind. Anyway I used https://mickzijdel.com/blog/2025-05-19-setting-up-a-debugger-for-ruby-on-rails-8/ for my setup. First I installed the vs code extension VSCode rdbg ruby debugger by Koichi Sasada. Next I added these to my gemfile
gem "rdbg" # Ruby debug integration
gem "foreman" # Necessary for the procfile to work
So next was the Procfile.dev in the root of the project. Replace the original web: line with this
web: RUBY_DEBUG_OPEN=TRUE RUBY_DEBUG_NONSTOP=TRUE rdbg --command --open --stop-at-load -- bundle exec bin/rails server -p 3000
css: bin/rails tailwindcss:watch
So here are the files for reference.
First Tasks
{
"version": "2.0.0",
"tasks": [
{
"label": "runBinDev",
"type": "shell",
"command": "bin/dev",
"isBackground": true,
"problemMatcher": [
{
"owner": "custom-rdbg-task",
"pattern": [
{
"regexp": "^.*DEBUGGER: Debugger can attach.*$",
"kind": "info",
"file": 1,
"location": 1,
"message": 0
}
],
"background": {
"activeOnStart": true,
"beginsPattern": "^.*DEBUGGER: Debugger can attach.*$",
"endsPattern": "^.*DEBUGGER: Debugger can attach.*$"
}
}
],
"presentation": {
"reveal": "always",
"panel": "dedicated",
"clear": true
},
"group": {
"kind": "build",
"isDefault": false
}
}
]
}
Now launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "rdbg",
"name": "Run bin/dev & Attach with rdbg",
"request": "attach",
"preLaunchTask": "runBinDev",
"localfs": true,
"localfsMap": "${workspaceFolder}:${workspaceFolder}"
},
{
"type": "rdbg",
"name": "Attach with rdbg",
"request": "attach",
"localfs": true,
"localfsMap": "${workspaceFolder}:${workspaceFolder}"
}
]
}
Writing a Test Case
So wanted to get a test case going because of the lack of type checking. When you generate a new component you get an empty test case. So for
rails generate component PageHeader
IWe get a app/component/page_header_component.html.erb and app/component/page_header_component.rb which I edited to be
<header class="relative mb-1 bg-(--color-nav-background) pt-4 pb-4 pl-12 text-center sm:pl-4">
<%= render PageHeaderButtonComponent.new %>
<h1 class="mb-2 text-center text-3xl font-bold text-(--color-title)">
<%= @title %>
</h1>
<% if @subtitle.present? %>
<p class="mb-4 text-center text-[1.2rem] font-normal text-(--color-subtitle)">
<%= @subtitle %>
</p>
<% end %>
<%= render PageHeaderNavComponent.new %>
</header>
And
class PageHeaderComponent < ViewComponent::Base
def initialize(title:, subtitle: nil)
@title = title
@subtitle = subtitle
end
end
But we also get a
# frozen_string_literal: true
require "test_helper"
class PageHeaderHeaderComponentTest < ViewComponent::TestCase
def test_component_renders_something_useful
# assert_equal(
# %(<span>Hello, components!</span>),
# render_inline(PageHeaderHeaderComponent.new(message: "Hello, components!")).css("span").to_html
# )
end
end
This I adapted to be
# frozen_string_literal: true
require "test_helper"
class PageHeaderComponentTest < ViewComponent::TestCase
def test_component_renders_something_useful
rendered = render_inline(PageHeaderComponent.new(title: "my title", subtitle: "my subtitle"))
assert_equal "my title", rendered.css("h1").text.strip
assert_equal "my subtitle", rendered.css("p").text.strip
assert rendered.at_css("button[data-controller='theme-toggle']").present?
end
end
I found I had not done the following
- Defined view_components correctly in the gemfile as it needs test
- Not modified test_helper.rb
So for the gemfile we needed
group :development, :test do
# view_component for building reusable view components [https://viewcomponent.org/]
gem "view_component"
end
And for the test_helper.rb, I am assuming this is autoloading, I need to have
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
require "rails/test_help"
require "view_component/test_case"
module ActiveSupport
class TestCase
# Run tests in parallel with specified workers
parallelize(workers: :number_of_processors)
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
end