Back to skills
SkillHub ClubShip Full StackFull Stack

moai-lang-ruby

Ruby 3.3+ development specialist covering Rails 7.2, ActiveRecord, Hotwire/Turbo, and modern Ruby patterns. Use when developing Ruby APIs, web applications, or Rails projects.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
2
Hot score
79
Updated
March 20, 2026
Overall rating
C1.6
Composite score
1.6
Best-practice grade
B81.2

Install command

npx @skill-hub/cli install junseokandylee-rallyapp-moai-lang-ruby

Repository

junseokandylee/RallyApp

Skill path: .claude/skills/moai-lang-ruby

Ruby 3.3+ development specialist covering Rails 7.2, ActiveRecord, Hotwire/Turbo, and modern Ruby patterns. Use when developing Ruby APIs, web applications, or Rails projects.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: junseokandylee.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install moai-lang-ruby into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/junseokandylee/RallyApp before adding moai-lang-ruby to shared team environments
  • Use moai-lang-ruby for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: moai-lang-ruby
description: Ruby 3.3+ development specialist covering Rails 7.2, ActiveRecord, Hotwire/Turbo, and modern Ruby patterns. Use when developing Ruby APIs, web applications, or Rails projects.
version: 1.0.0
updated: 2025-12-07
status: active
allowed-tools: Read, Grep, Glob, Bash, mcp__context7__resolve-library-id, mcp__context7__get-library-docs
---

## Quick Reference (30 seconds)

Ruby 3.3+ Development Specialist - Rails 7.2, ActiveRecord, Hotwire/Turbo, RSpec, and modern Ruby patterns.

Auto-Triggers: `.rb` files, `Gemfile`, `Rakefile`, `config.ru`, Rails/Ruby discussions

Core Capabilities:
- Ruby 3.3 Features: YJIT production-ready, pattern matching, Data class, endless methods
- Web Framework: Rails 7.2 with Turbo, Stimulus, ActiveRecord
- Frontend: Hotwire (Turbo + Stimulus) for SPA-like experiences
- Testing: RSpec with factories, request specs, system specs
- Background Jobs: Sidekiq with ActiveJob
- Package Management: Bundler with Gemfile
- Code Quality: RuboCop with Rails cops
- Database: ActiveRecord with migrations, associations, scopes

### Quick Patterns

Rails Controller:
```ruby
class UsersController < ApplicationController
  before_action :set_user, only: %i[show edit update destroy]

  def index
    @users = User.all
  end

  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        format.html { redirect_to @user, notice: "User was successfully created." }
        format.turbo_stream
      else
        format.html { render :new, status: :unprocessable_entity }
      end
    end
  end

  private

  def set_user
    @user = User.find(params[:id])
  end

  def user_params
    params.require(:user).permit(:name, :email)
  end
end
```

ActiveRecord Model:
```ruby
class User < ApplicationRecord
  has_many :posts, dependent: :destroy
  has_one :profile, dependent: :destroy

  validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :name, presence: true, length: { minimum: 2, maximum: 100 }

  scope :active, -> { where(active: true) }
  scope :recent, -> { order(created_at: :desc) }

  def full_name
    "#{first_name} #{last_name}".strip
  end
end
```

RSpec Test:
```ruby
RSpec.describe User, type: :model do
  describe "validations" do
    it { is_expected.to validate_presence_of(:email) }
    it { is_expected.to validate_uniqueness_of(:email) }
  end

  describe "#full_name" do
    let(:user) { build(:user, first_name: "John", last_name: "Doe") }

    it "returns the full name" do
      expect(user.full_name).to eq("John Doe")
    end
  end
end
```

---

## Implementation Guide (5 minutes)

### Ruby 3.3 New Features

YJIT (Production-Ready):
- Enabled by default in Ruby 3.3
- 15-20% performance improvement for Rails apps
- Enable: `ruby --yjit` or `RUBY_YJIT_ENABLE=1`
- Check status: `RubyVM::YJIT.enabled?`
- Memory optimization with better code caching

Pattern Matching (case/in):
```ruby
def process_response(response)
  case response
  in { status: "ok", data: data }
    puts "Success: #{data}"
  in { status: "error", message: msg }
    puts "Error: #{msg}"
  in { status: status } if %w[pending processing].include?(status)
    puts "In progress..."
  else
    puts "Unknown response"
  end
end

# Array pattern matching
case [1, 2, 3]
in [first, *rest]
  puts "First: #{first}, Rest: #{rest}"
end

# Hash pattern matching with guard
case { name: "John", age: 25 }
in { name:, age: } if age >= 18
  puts "Adult: #{name}"
end
```

Data Class (Immutable Structs):
```ruby
# Define immutable data class
User = Data.define(:name, :email) do
  def greeting
    "Hello, #{name}!"
  end
end

user = User.new(name: "John", email: "[email protected]")
user.name         # => "John"
user.greeting     # => "Hello, John!"
# user.name = "Jane"  # => FrozenError

# With default values
Point = Data.define(:x, :y) do
  def self.origin
    new(x: 0, y: 0)
  end
end
```

Endless Method Definition:
```ruby
class Calculator
  # Single expression methods
  def add(a, b) = a + b
  def multiply(a, b) = a * b

  # With method chaining
  def double(n) = n * 2
  def square(n) = n ** 2

  # Predicate methods
  def positive?(n) = n > 0
  def even?(n) = n.even?
end
```

### Rails 7.2 Patterns

Application Setup:
```ruby
# Gemfile
source "https://rubygems.org"

gem "rails", "~> 7.2.0"
gem "pg", "~> 1.5"
gem "puma", ">= 6.0"
gem "turbo-rails"
gem "stimulus-rails"
gem "jbuilder"
gem "redis", ">= 5.0"
gem "sidekiq", "~> 7.0"

group :development, :test do
  gem "rspec-rails", "~> 7.0"
  gem "factory_bot_rails"
  gem "faker"
  gem "rubocop-rails", require: false
end

group :test do
  gem "capybara"
  gem "shoulda-matchers"
end
```

Model with Concerns:
```ruby
# app/models/concerns/sluggable.rb
module Sluggable
  extend ActiveSupport::Concern

  included do
    before_validation :generate_slug, on: :create
    validates :slug, presence: true, uniqueness: true
  end

  def to_param
    slug
  end

  private

  def generate_slug
    self.slug = title.parameterize if title.present? && slug.blank?
  end
end

# app/models/post.rb
class Post < ApplicationRecord
  include Sluggable

  belongs_to :user
  has_many :comments, dependent: :destroy
  has_many_attached :images

  validates :title, presence: true, length: { minimum: 5 }
  validates :content, presence: true

  scope :published, -> { where(published: true) }
  scope :by_user, ->(user) { where(user: user) }
end
```

Service Objects:
```ruby
# app/services/user_registration_service.rb
class UserRegistrationService
  def initialize(user_params)
    @user_params = user_params
  end

  def call
    user = User.new(@user_params)

    ActiveRecord::Base.transaction do
      user.save!
      create_profile(user)
      send_welcome_email(user)
    end

    Result.new(success: true, user: user)
  rescue ActiveRecord::RecordInvalid => e
    Result.new(success: false, errors: e.record.errors)
  end

  private

  def create_profile(user)
    user.create_profile!(bio: "New user")
  end

  def send_welcome_email(user)
    UserMailer.welcome(user).deliver_later
  end

  Result = Data.define(:success, :user, :errors) do
    def success? = success
    def failure? = !success
  end
end
```

### Hotwire (Turbo + Stimulus)

Turbo Frames:
```erb
<!-- app/views/posts/index.html.erb -->
<%= turbo_frame_tag "posts" do %>
  <% @posts.each do |post| %>
    <%= render post %>
  <% end %>
<% end %>

<!-- app/views/posts/_post.html.erb -->
<%= turbo_frame_tag dom_id(post) do %>
  <article class="post">
    <h2><%= link_to post.title, post %></h2>
    <p><%= truncate(post.content, length: 200) %></p>
    <%= link_to "Edit", edit_post_path(post) %>
  </article>
<% end %>
```

Turbo Streams:
```ruby
# app/controllers/posts_controller.rb
def create
  @post = current_user.posts.build(post_params)

  respond_to do |format|
    if @post.save
      format.turbo_stream
      format.html { redirect_to @post }
    else
      format.html { render :new, status: :unprocessable_entity }
    end
  end
end

# app/views/posts/create.turbo_stream.erb
<%= turbo_stream.prepend "posts", @post %>
<%= turbo_stream.update "new_post", partial: "posts/form", locals: { post: Post.new } %>
```

Stimulus Controller:
```javascript
// app/javascript/controllers/form_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["input", "submit", "output"]
  static values = { url: String }

  connect() {
    this.validate()
  }

  validate() {
    const isValid = this.inputTargets.every(input => input.value.length > 0)
    this.submitTarget.disabled = !isValid
  }

  async submit(event) {
    event.preventDefault()

    const response = await fetch(this.urlValue, {
      method: "POST",
      body: new FormData(this.element),
      headers: { "Accept": "text/vnd.turbo-stream.html" }
    })

    if (response.ok) {
      this.element.reset()
    }
  }
}
```

### RSpec Testing Patterns

Model Specs:
```ruby
# spec/models/user_spec.rb
RSpec.describe User, type: :model do
  describe "associations" do
    it { is_expected.to have_many(:posts).dependent(:destroy) }
    it { is_expected.to have_one(:profile).dependent(:destroy) }
  end

  describe "validations" do
    subject { build(:user) }

    it { is_expected.to validate_presence_of(:email) }
    it { is_expected.to validate_uniqueness_of(:email).case_insensitive }
    it { is_expected.to validate_length_of(:name).is_at_least(2).is_at_most(100) }
  end

  describe "scopes" do
    describe ".active" do
      let!(:active_user) { create(:user, active: true) }
      let!(:inactive_user) { create(:user, active: false) }

      it "returns only active users" do
        expect(described_class.active).to contain_exactly(active_user)
      end
    end
  end

  describe "#full_name" do
    context "when both names are present" do
      let(:user) { build(:user, first_name: "John", last_name: "Doe") }

      it "returns the full name" do
        expect(user.full_name).to eq("John Doe")
      end
    end

    context "when last name is missing" do
      let(:user) { build(:user, first_name: "John", last_name: nil) }

      it "returns only first name" do
        expect(user.full_name).to eq("John")
      end
    end
  end
end
```

Request Specs:
```ruby
# spec/requests/posts_spec.rb
RSpec.describe "Posts", type: :request do
  let(:user) { create(:user) }

  before { sign_in user }

  describe "GET /posts" do
    let!(:posts) { create_list(:post, 3, user: user) }

    it "returns a successful response" do
      get posts_path
      expect(response).to have_http_status(:ok)
    end

    it "displays all posts" do
      get posts_path
      posts.each do |post|
        expect(response.body).to include(post.title)
      end
    end
  end

  describe "POST /posts" do
    let(:valid_params) { { post: attributes_for(:post) } }
    let(:invalid_params) { { post: { title: "" } } }

    context "with valid parameters" do
      it "creates a new post" do
        expect {
          post posts_path, params: valid_params
        }.to change(Post, :count).by(1)
      end

      it "redirects to the created post" do
        post posts_path, params: valid_params
        expect(response).to redirect_to(Post.last)
      end
    end

    context "with invalid parameters" do
      it "does not create a new post" do
        expect {
          post posts_path, params: invalid_params
        }.not_to change(Post, :count)
      end

      it "returns unprocessable entity status" do
        post posts_path, params: invalid_params
        expect(response).to have_http_status(:unprocessable_entity)
      end
    end
  end
end
```

Factory Bot Patterns:
```ruby
# spec/factories/users.rb
FactoryBot.define do
  factory :user do
    sequence(:email) { |n| "user#{n}@example.com" }
    name { Faker::Name.name }
    password { "password123" }
    active { true }

    trait :inactive do
      active { false }
    end

    trait :admin do
      role { :admin }
    end

    trait :with_posts do
      transient do
        posts_count { 3 }
      end

      after(:create) do |user, evaluator|
        create_list(:post, evaluator.posts_count, user: user)
      end
    end

    factory :admin_user, traits: [:admin]
  end
end
```

### Sidekiq Background Jobs

Job Definition:
```ruby
# app/jobs/process_order_job.rb
class ProcessOrderJob < ApplicationJob
  queue_as :default
  retry_on ActiveRecord::Deadlocked, wait: 5.seconds, attempts: 3
  discard_on ActiveJob::DeserializationError

  def perform(order_id)
    order = Order.find(order_id)

    ActiveRecord::Base.transaction do
      order.process!
      order.update!(processed_at: Time.current)
      OrderMailer.confirmation(order).deliver_later
    end
  end
end

# Sidekiq configuration
# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
  config.redis = { url: ENV.fetch("REDIS_URL", "redis://localhost:6379/1") }
end

Sidekiq.configure_client do |config|
  config.redis = { url: ENV.fetch("REDIS_URL", "redis://localhost:6379/1") }
end
```

### ActiveRecord Advanced Patterns

Scopes and Query Objects:
```ruby
class Post < ApplicationRecord
  scope :published, -> { where(published: true) }
  scope :recent, -> { order(created_at: :desc) }
  scope :by_author, ->(author) { where(author: author) }
  scope :search, ->(query) { where("title ILIKE ?", "%#{query}%") }

  # Complex scope with joins
  scope :with_comments, -> {
    joins(:comments).group(:id).having("COUNT(comments.id) > 0")
  }

  # Scope returning specific columns
  scope :titles_only, -> { select(:id, :title) }
end

# Query Object
class PostSearchQuery
  def initialize(relation = Post.all)
    @relation = relation
  end

  def call(params)
    @relation
      .then { |r| filter_by_status(r, params[:status]) }
      .then { |r| filter_by_date(r, params[:start_date], params[:end_date]) }
      .then { |r| search_by_title(r, params[:query]) }
      .then { |r| paginate(r, params[:page], params[:per_page]) }
  end

  private

  def filter_by_status(relation, status)
    return relation if status.blank?
    relation.where(status: status)
  end

  def filter_by_date(relation, start_date, end_date)
    relation = relation.where("created_at >= ?", start_date) if start_date
    relation = relation.where("created_at <= ?", end_date) if end_date
    relation
  end

  def search_by_title(relation, query)
    return relation if query.blank?
    relation.where("title ILIKE ?", "%#{query}%")
  end

  def paginate(relation, page, per_page)
    page ||= 1
    per_page ||= 25
    relation.limit(per_page).offset((page.to_i - 1) * per_page.to_i)
  end
end
```

---

## Advanced Implementation (10+ minutes)

For comprehensive coverage including:
- Production deployment patterns (Docker, Kubernetes)
- Advanced ActiveRecord patterns (polymorphic, STI)
- Action Cable real-time features
- Performance optimization techniques
- Security best practices
- CI/CD integration patterns

See:
- [reference.md](reference.md) - Complete reference documentation
- [examples.md](examples.md) - Production-ready code examples

---

## Context7 Library Mappings

```
/rails/rails - Ruby on Rails web framework
/rspec/rspec - RSpec testing framework
/hotwired/turbo-rails - Turbo for Rails
/hotwired/stimulus-rails - Stimulus for Rails
/sidekiq/sidekiq - Background job processing
/rubocop/rubocop - Ruby style guide enforcement
/thoughtbot/factory_bot - Test data factories
```

---

## Works Well With

- `moai-domain-backend` - REST API and web application architecture
- `moai-domain-database` - SQL patterns and ActiveRecord optimization
- `moai-quality-testing` - TDD and testing strategies
- `moai-essentials-debug` - AI-powered debugging
- `moai-foundation-trust` - TRUST 5 quality principles

---

## Troubleshooting

Common Issues:

Ruby Version Check:
```bash
ruby --version  # Should be 3.3+
ruby -e "puts RubyVM::YJIT.enabled?"  # Check YJIT status
```

Rails Version Check:
```bash
rails --version  # Should be 7.2+
bundle exec rails about  # Full environment info
```

Database Connection Issues:
- Check `config/database.yml` configuration
- Ensure PostgreSQL/MySQL service is running
- Run `rails db:create` if database doesn't exist

Asset Pipeline Issues:
```bash
# Precompile assets
rails assets:precompile

# Clear asset cache
rails assets:clobber
```

RSpec Setup Issues:
```bash
# Install RSpec
rails generate rspec:install

# Run specific test
bundle exec rspec spec/models/user_spec.rb

# Run with verbose output
bundle exec rspec --format documentation
```

Turbo/Stimulus Issues:
```bash
# Rebuild JavaScript
rails javascript:install:esbuild

# Clear Turbo cache
rails turbo:install
```

---

Last Updated: 2025-12-07
Status: Active (v1.0.0)
moai-lang-ruby | SkillHub