CtrlK
BlogDocsLog inGet started
Tessl Logo

igmarin/rails-agent-skills

Curated library of 41 public AI agent skills for Ruby on Rails development. Organized by category: planning, testing, code-quality, ddd, engines, infrastructure, api, patterns, context, and orchestration. Covers code review, architecture, security, testing (RSpec), engines, service objects, DDD patterns, and TDD automation. Repository workflows remain documented in GitHub but are intentionally excluded from the Tessl tile.

95

1.77x
Quality

93%

Does it follow best practices?

Impact

96%

1.77x

Average score across 41 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

EXAMPLES.mdskills/code-quality/implement-authorization/

Authorization Examples

Complete code examples for Pundit and CanCanCan implementations.

Pundit Examples

Policy Class

# app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
  def update?
    user.admin? || record.user_id == user.id
  end

  def destroy?
    user.admin?
  end

  def create?
    user.present?
  end
end

Controller Integration

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post, only: [:update, :destroy]

  def update
    authorize @post
    if @post.update(post_params)
      redirect_to @post, notice: 'Updated successfully'
    else
      render :edit, status: :unprocessable_entity
    end
  end

  def destroy
    authorize @post
    @post.destroy
    redirect_to posts_path, notice: 'Deleted successfully'
  end

  private

  def set_post
    @post = Post.find(params[:id])
  end
end

CanCanCan Examples

Ability Class

# app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user

    if user.admin?
      can :manage, :all
    else
      can :read, :all
      can :update, Post, user_id: user.id
      can :destroy, Post, user_id: user.id
      can :create, Post
    end
  end
end

Controller Integration

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  load_and_authorize_resource

  def update
    if @post.update(post_params)
      redirect_to @post, notice: 'Updated successfully'
    else
      render :edit, status: :unprocessable_entity
    end
  end
end

Testing Examples

Pundit Policy Spec

# spec/policies/post_policy_spec.rb
require 'rails_helper'

RSpec.describe PostPolicy do
  subject { described_class.new(user, post) }

  let(:post) { create(:post, user: owner) }
  let(:owner) { create(:user) }

  context 'as the post owner' do
    let(:user) { owner }

    it { is_expected.to permit_action(:update) }
    it { is_expected.to permit_action(:destroy) }
    it { is_expected.to permit_action(:create) }
  end

  context 'as a different user' do
    let(:user) { create(:user) }

    it { is_expected.not_to permit_action(:update) }
    it { is_expected.not_to permit_action(:destroy) }
    it { is_expected.to permit_action(:create) }
  end

  context 'as an admin' do
    let(:user) { create(:user, :admin) }

    it { is_expected.to permit_action(:update) }
    it { is_expected.to permit_action(:destroy) }
    it { is_expected.to permit_action(:create) }
  end

  context 'as a guest' do
    let(:user) { nil }

    it { is_expected.not_to permit_action(:update) }
    it { is_expected.not_to permit_action(:destroy) }
    it { is_expected.not_to permit_action(:create) }
  end
end

Request Spec

# spec/requests/posts_spec.rb
require 'rails_helper'

RSpec.describe 'PATCH /posts/:id', type: :request do
  let(:post) { create(:post, user: owner) }
  let(:owner) { create(:user) }

  it 'allows the owner to update' do
    sign_in owner
    patch post_path(post), params: { post: { title: 'New Title' } }
    expect(response).to have_http_status(:ok)
    expect(post.reload.title).to eq('New Title')
  end

  it 'denies a guest' do
    patch post_path(post), params: { post: { title: 'New Title' } }
    expect(response).to have_http_status(:unauthorized)
  end

  it 'denies a different user' do
    sign_in create(:user)
    patch post_path(post), params: { post: { title: 'New Title' } }
    expect(response).to have_http_status(:forbidden)
  end

  it 'allows an admin to update any post' do
    sign_in create(:user, :admin)
    patch post_path(post), params: { post: { title: 'Admin Updated' } }
    expect(response).to have_http_status(:ok)
  end
end

Unauthorized Manual Verification

Use this shape in the final implementation report after automated specs pass.

# Rails console — Pundit
user = User.find_by!(email: 'member@example.com')
post = Post.find_by!(slug: 'admin-only')

Pundit.authorize(user, post, :update?)
# raises Pundit::NotAuthorizedError
# Rails console — CanCanCan
user = User.find_by!(email: 'member@example.com')
post = Post.find_by!(slug: 'admin-only')
ability = Ability.new(user)

ability.authorize! :update, post
# raises CanCan::AccessDenied
# HTTP check
curl -i -X PATCH http://localhost:3000/posts/admin-only \
  -H "Authorization: Bearer <token_for_user_without_permission>"
# Expected: 403 Forbidden, or the app's configured denied-access response

Shared Examples

# spec/support/shared_examples/authorization.rb
RSpec.shared_examples 'requires authentication' do |action|
  it "requires authentication for #{action}" do
    public_send(action)
    expect(response).to redirect_to(new_user_session_path)
  end
end

RSpec.shared_examples 'requires authorization' do |action, resource_owner_method|
  it "requires authorization for #{action}" do
    other_user = create(:user)
    resource = create(described_class.controller_name.singularize, user: other_user)
    sign_in create(:user)
    public_send(action, params: { id: resource.id })
    expect(response).to have_http_status(:forbidden)
  end
end

Usage in Controllers

# spec/controllers/posts_controller_spec.rb
require 'rails_helper'

RSpec.describe PostsController do
  it_behaves_like 'requires authentication', :get, :index
  it_behaves_like 'requires authorization', :patch, :update
end

skills

code-quality

README.md

server.json

tile.json