tessl install github:ThibautBaissac/rails_ai_agents --skill rails-controllergithub.com/ThibautBaissac/rails_ai_agents
Creates Rails controllers with TDD approach - request spec first, then implementation. Use when creating new controllers, adding controller actions, implementing CRUD operations, or when user mentions controllers, routes, or API endpoints.
Review Score
87%
Validation Score
13/16
Implementation Score
77%
Activation Score
100%
Creates RESTful controllers following project conventions with request specs first.
spec/requests/This project uses:
authorize @resource, policy_scope(Model))current_account# spec/requests/[resources]_spec.rb
RSpec.describe "[Resources]", type: :request do
let(:user) { create(:user) }
let(:other_user) { create(:user) }
before { sign_in user, scope: :user }
describe "GET /[resources]" do
let!(:resource) { create(:[resource], account: user.account) }
let!(:other_resource) { create(:[resource], account: other_user.account) }
it "returns http success" do
get [resources]_path
expect(response).to have_http_status(:success)
end
it "shows only current_user's resources (multi-tenant)" do
get [resources]_path
expect(response.body).to include(resource.name)
expect(response.body).not_to include(other_resource.name)
end
end
describe "GET /[resources]/:id" do
let!(:resource) { create(:[resource], account: user.account) }
it "returns http success" do
get [resource]_path(resource)
expect(response).to have_http_status(:success)
end
end
describe "POST /[resources]" do
let(:valid_params) { { [resource]: attributes_for(:[resource]) } }
it "creates a new resource" do
expect {
post [resources]_path, params: valid_params
}.to change([Resource], :count).by(1)
end
it "assigns to current_account" do
post [resources]_path, params: valid_params
expect([Resource].last.account).to eq(user.account)
end
end
describe "authorization" do
let!(:other_resource) { create(:[resource], account: other_user.account) }
it "returns 404 for unauthorized access" do
get [resource]_path(other_resource)
expect(response).to have_http_status(:not_found)
end
end
endbundle exec rspec spec/requests/[resources]_spec.rb# app/controllers/[resources]_controller.rb
class [Resources]Controller < ApplicationController
before_action :set_[resource], only: [:show, :edit, :update, :destroy]
def index
authorize [Resource], :index?
@pagy, resources = pagy(policy_scope([Resource]).order(created_at: :desc))
@[resources] = resources.map { |r| [Resource]Presenter.new(r) }
end
def show
authorize @[resource]
@[resource] = [Resource]Presenter.new(@[resource])
end
def new
@[resource] = current_account.[resources].build
authorize @[resource]
end
def create
@[resource] = current_account.[resources].build([resource]_params)
authorize @[resource]
if @[resource].save
redirect_to [resources]_path, notice: "[Resource] created successfully"
else
render :new, status: :unprocessable_entity
end
end
def edit
authorize @[resource]
end
def update
authorize @[resource]
if @[resource].update([resource]_params)
redirect_to @[resource], notice: "[Resource] updated successfully"
else
render :edit, status: :unprocessable_entity
end
end
def destroy
authorize @[resource]
@[resource].destroy
redirect_to [resources]_path, notice: "[Resource] deleted successfully"
end
private
def set_[resource]
@[resource] = policy_scope([Resource]).find(params[:id])
end
def [resource]_params
params.require(:[resource]).permit(:name, :field1, :field2)
end
endbundle exec rspec spec/requests/[resources]_spec.rbFor nested routes like settings/accounts:
# app/controllers/settings/accounts_controller.rb
module Settings
class AccountsController < ApplicationController
before_action :set_account
def show
authorize @account
end
private
def set_account
@account = current_account
end
end
enddef create
@resource = current_account.resources.build(resource_params)
authorize @resource
if @resource.save
respond_to do |format|
format.html { redirect_to resources_path, notice: "Created" }
format.turbo_stream do
flash.now[:notice] = "Created"
@pagy, @resources = pagy(policy_scope(Resource).order(created_at: :desc))
render turbo_stream: [
turbo_stream.replace("resources-list", partial: "resources/list"),
turbo_stream.update("modal", "")
]
end
end
else
render :new, status: :unprocessable_entity
end
endauthorize on every actionpolicy_scope for queries