DEV Community

Cover image for Beyond Autocomplete: How Cursor AI is Helping Standardize and Write Ruby on Rails Code
Cirdes Henrique
Cirdes Henrique

Posted on

5 1 1

Beyond Autocomplete: How Cursor AI is Helping Standardize and Write Ruby on Rails Code

PT-BR

Context

Until recently, aside from code autocomplete capabilities, I had yet to see a truly reliable way to generate AI-powered code that would be useful for my company, Linkana.

I agree with DHH when he says that AI is merely an excellent junior developer—sometimes producing overly complex code or introducing subtle, hard-to-spot bugs. Whether this will change in the future is uncertain. What matters is that today, by providing good context, we can get AI to help us write high-quality code.

Cursor AI

In September of last year, Rafael França (Rails Core) spent a few days in São Paulo and showed me that he was using Cursor instead of VSCode. I decided to switch to Cursor AI, and since then, I’ve been taking advantage of its superior autocomplete capabilities and its ability to explain code snippets. So far, nothing too sophisticated.

Cursor Rules

Over the past few weeks, I discovered the Rules for AI feature—a way to teach AI how to "think." This allows us to guide Cursor to behave like a good junior developer would: recognizing code patterns, replicating them in similar contexts, and ensuring consistency across the project.

Migrating from RSpec to MiniTest

Last year, we decided to migrate to MiniTest. We started writing new tests in MiniTest, but we still have hundreds of files written in RSpec. As expected, no one wants to prioritize rewriting tests, making this migration extremely slow.

To speed up the process, we created a structure like this, allowing us to define specific rules for different contexts within our code:

.cursor/rules/
  ├── rails8.mdc
  ├── models/
  │   ├── active_record.mdc
  │   ├── tests.mdc
  │   └── postgresql.mdc
  ├── controllers/
  │   ├── api.mdc
  │   └── tests.mdc
  └── views/
      ├── phlex.mdc
      └── components.mdc
Enter fullscreen mode Exit fullscreen mode

Let's take a look at models/tests.mdc:

---
description: ""
globs: test/models/**/*.rb
alwaysApply: true
---

# Rails Model Testing with Minitest and FactoryBot

## General Structure

- Each model should have its own corresponding test file (e.g., `user_test.rb` for the `User` model)
- Test files should be organized within the `test/models` directory, mirroring the application structure
- Subdirectories can be used to organize specific components (e.g., `test/models/documents/`)

## Test Configuration and Organization

- Each file should include `require "test_helper"` at the beginning
- The test class should inherit from `ActiveSupport::TestCase`
- Tests should be written using `test "description" do` instead of `def test_description`
- Related tests should be grouped together
- The `setup` method should be used to create test data with factories

## Using Factories

- Factories should be defined in `test/factories/`
- Use `create(:factory_name)` for persisted records and `build(:factory_name)` for non-persisted ones
- Use `build_stubbed(:factory_name)` when persistence is not needed
- Use descriptive traits for factory variations (e.g., `:with_buyer`, `:not_clear`)

## Example Structure

# frozen_string_literal: true

require "test_helper"

class ModelNameTest < ActiveSupport::TestCase
  def setup
    @model = create(:model_name, :with_trait)
  end

  test "associations" do
    assert_respond_to(@model, :association_name)
    # Test other associations...
  end

  test "validations" do
    invalid_model = build(:model_name, required_attribute: nil)
    refute(invalid_model.valid?)
    assert_equal(invalid_model.errors[:required_attribute], ["expected error message"])
  end
end

## Style and Best Practices

- Tests should be independent and isolated
- Avoid fixtures whenever possible, preferring factories
- Use meaningful test data to clearly express intent
- Test edge cases and failure scenarios
- Follow the "Arrange, Act, Assert" pattern
- Cover both positive and negative cases
Enter fullscreen mode Exit fullscreen mode

This rule establishes standards for things like using factories, organizing tests, and formatting test "instance_method" do statements. Beyond guiding AI in writing tests, these rules help maintain project standardization and serve as documentation of best practices for both the human team and the AI itself.

The most interesting part is that I generated this rule with a single prompt in Cursor. I provided documentation and examples from my own code, and the AI automatically created the instructions—I only had to make minor adjustments.

Using It in Practice

With the rules in place, it was time to test them in practice. When I asked Cursor to migrate an RSpec test to MiniTest, it automatically identified the applicable rule based on globs: test/models/**/*.rb.

Cursor applying the rule

To validate the process, I migrated the files one by one. The only error I encountered was related to language—Cursor expected error messages in English, but my application is in Portuguese.

Language error in generated tests

In the end, I was able to open a Pull Request with minimal effort and the expected quality. Will this PR be merged without any requested changes?

PR created on GitHub

API Trace View

How I Cut 22.3 Seconds Off an API Call with Sentry 🕒

Struggling with slow API calls? Dan Mindru walks through how he used Sentry's new Trace View feature to shave off 22.3 seconds from an API call.

Get a practical walkthrough of how to identify bottlenecks, split tasks into multiple parallel tasks, identify slow AI model calls, and more.

Read more →

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

AWS GenAI LIVE!

GenAI LIVE! is a dynamic live-streamed show exploring how AWS and our partners are helping organizations unlock real value with generative AI.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️