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
0
Hot score
74
Updated
March 20, 2026
Overall rating
C2.7
Composite score
2.7
Best-practice grade
B73.6

Install command

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

Repository

junseokandylee/ClaudeAutomate

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/ClaudeAutomate 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?`

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
```

Data Class (Immutable Structs):
```ruby
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!"
```

Endless Method Definition:
```ruby
class Calculator
  def add(a, b) = a + b
  def multiply(a, b) = a * b
  def positive?(n) = n > 0
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 "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) }
end
```

Service Objects:
```ruby
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>
  </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"]

  connect() {
    this.validate()
  }

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

### RSpec Testing Basics

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" }

    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
  end
end
```

---

## Advanced Implementation (10+ minutes)

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

See:
- [Advanced Patterns](modules/advanced-patterns.md) - Production patterns and advanced features
- [Testing Patterns](modules/testing-patterns.md) - Complete RSpec testing guide

---

## 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-workflow-testing` - TDD and testing strategies
- `moai-essentials-debug` - AI-powered debugging
- `moai-foundation-quality` - 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
rails assets:precompile
rails assets:clobber
```

RSpec Setup Issues:
```bash
rails generate rspec:install
bundle exec rspec spec/models/user_spec.rb
bundle exec rspec --format documentation
```

Turbo/Stimulus Issues:
```bash
rails javascript:install:esbuild
rails turbo:install
```

---

Last Updated: 2025-12-07
Status: Active (v1.0.0)


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### modules/advanced-patterns.md

```markdown
# Ruby Advanced Patterns

## 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
```

## Production Deployment Patterns

Docker Configuration:
```dockerfile
# Dockerfile
FROM ruby:3.3-slim

RUN apt-get update -qq && apt-get install -y \
    build-essential \
    libpq-dev \
    nodejs \
    npm \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY Gemfile Gemfile.lock ./
RUN bundle config set --local deployment 'true' && \
    bundle config set --local without 'development test' && \
    bundle install

COPY . .

RUN bundle exec rails assets:precompile

ENV RAILS_ENV=production
ENV RAILS_LOG_TO_STDOUT=true

EXPOSE 3000

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
```

Puma Configuration:
```ruby
# config/puma.rb
workers ENV.fetch("WEB_CONCURRENCY") { 2 }
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count

preload_app!

port ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }

plugin :tmp_restart

on_worker_boot do
  ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
end
```

## Advanced ActiveRecord Patterns

Polymorphic Associations:
```ruby
class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
  belongs_to :user
end

class Post < ApplicationRecord
  has_many :comments, as: :commentable, dependent: :destroy
end

class Image < ApplicationRecord
  has_many :comments, as: :commentable, dependent: :destroy
end
```

Single Table Inheritance (STI):
```ruby
class Vehicle < ApplicationRecord
  validates :brand, presence: true
end

class Car < Vehicle
  validates :doors, numericality: { greater_than: 0 }
end

class Motorcycle < Vehicle
  validates :engine_cc, numericality: { greater_than: 0 }
end
```

## Action Cable Real-Time Features

Channel Implementation:
```ruby
# app/channels/notification_channel.rb
class NotificationChannel < ApplicationCable::Channel
  def subscribed
    stream_from "notifications:#{current_user.id}"
  end

  def unsubscribed
    stop_all_streams
  end
end

# Broadcasting notifications
class NotificationService
  def self.notify(user, message)
    ActionCable.server.broadcast(
      "notifications:#{user.id}",
      { message: message, timestamp: Time.current.iso8601 }
    )
  end
end
```

## Performance Optimization

N+1 Query Prevention:
```ruby
# Bad: N+1 queries
User.all.each do |user|
  puts user.posts.count  # Executes a query for each user
end

# Good: Eager loading
User.includes(:posts).each do |user|
  puts user.posts.size  # Uses preloaded data
end

# Counter cache for counts
class Post < ApplicationRecord
  belongs_to :user, counter_cache: true
end

# Migration for counter cache
add_column :users, :posts_count, :integer, default: 0
```

Database Indexing:
```ruby
# migration
class AddIndexesToPosts < ActiveRecord::Migration[7.2]
  def change
    add_index :posts, :user_id
    add_index :posts, :status
    add_index :posts, [:status, :published_at]
    add_index :posts, :title, using: :gin, opclass: :gin_trgm_ops
  end
end
```

## Security Best Practices

Strong Parameters:
```ruby
class UsersController < ApplicationController
  private

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

CSRF Protection:
```ruby
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
end
```

SQL Injection Prevention:
```ruby
# Bad: Direct interpolation
User.where("email = '#{params[:email]}'")

# Good: Parameterized query
User.where(email: params[:email])
User.where("email = ?", params[:email])
```

## CI/CD Integration

GitHub Actions Workflow:
```yaml
# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: postgres
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.3'
          bundler-cache: true

      - name: Setup database
        env:
          DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
        run: |
          bundle exec rails db:create db:migrate

      - name: Run tests
        env:
          DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
        run: bundle exec rspec
```

```

### modules/testing-patterns.md

```markdown
# Ruby Testing Patterns

## RSpec Model Specs

Complete Model Testing:
```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
```

## RSpec Request Specs

API Testing:
```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

Advanced Factories:
```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

# spec/factories/posts.rb
FactoryBot.define do
  factory :post do
    sequence(:title) { |n| "Post Title #{n}" }
    content { Faker::Lorem.paragraphs(number: 3).join("\n\n") }
    user
    published { false }

    trait :published do
      published { true }
      published_at { Time.current }
    end

    trait :with_comments do
      transient do
        comments_count { 5 }
      end

      after(:create) do |post, evaluator|
        create_list(:comment, evaluator.comments_count, post: post)
      end
    end
  end
end
```

## System Specs with Capybara

End-to-End Testing:
```ruby
# spec/system/user_registration_spec.rb
RSpec.describe "User Registration", type: :system do
  before do
    driven_by(:selenium_chrome_headless)
  end

  it "allows a user to register" do
    visit new_user_registration_path

    fill_in "Email", with: "[email protected]"
    fill_in "Password", with: "password123"
    fill_in "Password confirmation", with: "password123"
    click_button "Sign up"

    expect(page).to have_content("Welcome! You have signed up successfully.")
    expect(User.find_by(email: "[email protected]")).to be_present
  end

  it "shows validation errors for invalid input" do
    visit new_user_registration_path

    fill_in "Email", with: "invalid-email"
    click_button "Sign up"

    expect(page).to have_content("Email is invalid")
  end
end
```

## Service Object Testing

Testing Service Layer:
```ruby
# spec/services/user_registration_service_spec.rb
RSpec.describe UserRegistrationService do
  describe "#call" do
    let(:valid_params) do
      {
        name: "John Doe",
        email: "[email protected]",
        password: "password123"
      }
    end

    context "with valid parameters" do
      it "creates a user" do
        result = described_class.new(valid_params).call

        expect(result.success?).to be true
        expect(result.user).to be_persisted
      end

      it "creates a profile for the user" do
        result = described_class.new(valid_params).call

        expect(result.user.profile).to be_present
      end

      it "sends a welcome email" do
        expect {
          described_class.new(valid_params).call
        }.to have_enqueued_mail(UserMailer, :welcome)
      end
    end

    context "with invalid parameters" do
      let(:invalid_params) { { email: "invalid" } }

      it "returns a failure result" do
        result = described_class.new(invalid_params).call

        expect(result.failure?).to be true
        expect(result.errors).to be_present
      end

      it "does not create a user" do
        expect {
          described_class.new(invalid_params).call
        }.not_to change(User, :count)
      end
    end
  end
end
```

## Shared Examples

Reusable Test Patterns:
```ruby
# spec/support/shared_examples/api_resource.rb
RSpec.shared_examples "an API resource" do
  it "returns JSON content type" do
    expect(response.content_type).to include("application/json")
  end

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

# Usage in specs
RSpec.describe "Users API", type: :request do
  describe "GET /api/users" do
    before { get api_users_path }

    it_behaves_like "an API resource"
  end
end
```

## Mocking and Stubbing

External Service Mocking:
```ruby
RSpec.describe PaymentService do
  describe "#charge" do
    let(:payment_gateway) { instance_double(Stripe::PaymentIntent) }

    before do
      allow(Stripe::PaymentIntent).to receive(:create).and_return(payment_gateway)
      allow(payment_gateway).to receive(:status).and_return("succeeded")
    end

    it "processes the payment successfully" do
      result = described_class.new.charge(amount: 1000, customer_id: "cus_123")

      expect(result).to be_success
    end
  end
end
```

```

moai-lang-ruby | SkillHub