Creates query objects for complex database queries following TDD. Use when encapsulating complex queries, aggregating statistics, building reports, or when user mentions queries, stats, dashboards, or data aggregation.
Install with Tessl CLI
npx tessl i github:fernandezbaptiste/rails_ai_agents --skill rails-query-object83
Does it follow best practices?
If you maintain this skill, you can automatically optimize it using the tessl CLI to improve its score:
npx tessl skill review --optimize ./path/to/skillValidation for skill structure
Creates query objects that encapsulate complex database queries with specs first.
spec/queries/app/queries/Query objects in this project:
user: or account:)ActiveRecord::Relation for chainability OR Hash for aggregationscall method for primary operation# spec/queries/[name]_query_spec.rb
RSpec.describe [Name]Query do
subject(:query) { described_class.new(account: account) }
let(:user) { create(:user) }
let(:account) { user.account }
let(:other_account) { create(:user).account }
# Test data for current account
let!(:resource1) { create(:resource, account: account) }
let!(:resource2) { create(:resource, account: account) }
# Test data for other account (should not appear)
let!(:other_resource) { create(:resource, account: other_account) }
describe "#initialize" do
it "requires an account parameter" do
expect { described_class.new }.to raise_error(ArgumentError)
end
it "stores the account" do
expect(query.account).to eq(account)
end
end
describe "#call" do
it "returns expected result type" do
expect(query.call).to be_a(ActiveRecord::Relation)
# OR for hash results:
# expect(query.call).to be_a(Hash)
end
it "only returns resources for the account (multi-tenant)" do
result = query.call
expect(result).to include(resource1, resource2)
expect(result).not_to include(other_resource)
end
end
describe "multi-tenant isolation" do
it "ensures account A cannot see account B data" do
other_query = described_class.new(account: other_account)
expect(query.call).not_to include(other_resource)
expect(other_query.call).not_to include(resource1)
end
end
endbundle exec rspec spec/queries/[name]_query_spec.rb# app/queries/[name]_query.rb
class [Name]Query
attr_reader :account
def initialize(account:)
@account = account
end
# Returns [description of result]
# @return [ActiveRecord::Relation<Resource>] OR [Hash]
def call
account.resources
.where(condition: value)
.order(created_at: :desc)
end
endbundle exec rspec spec/queries/[name]_query_spec.rb# app/queries/stale_leads_query.rb
class StaleLeadsQuery
attr_reader :account
def initialize(account:)
@account = account
end
def call
account.leads.stale
end
end# app/queries/dashboard_stats_query.rb
class DashboardStatsQuery
attr_reader :user, :account
def initialize(user:)
@user = user
@account = user.account
end
def upcoming_events(limit: 3)
account.events
.where("event_date >= ?", Date.today)
.order(event_date: :asc)
.limit(limit)
end
def pending_commissions_total
EventVendor
.joins(:event)
.where(events: { account_id: account.id })
.where(commission_status: :to_invoice)
.sum(:commission_value)
end
def top_vendors(limit: 5)
account.vendors
.left_joins(:event_vendors)
.select("vendors.*, COUNT(event_vendors.id) as events_count")
.group("vendors.id")
.order("events_count DESC")
.limit(limit)
end
def leads_by_status
account.leads.group(:status).count
end
end# app/queries/leads_by_status_query.rb
class LeadsByStatusQuery
attr_reader :account
def initialize(account:)
@account = account
end
def call
leads = account.leads.order(created_at: :desc)
result = Lead.statuses.keys.map(&:to_sym).index_with { [] }
leads.group_by(&:status).each do |status, status_leads|
result[status.to_sym] = status_leads
end
result
end
end# Simple query
def index
@leads_by_status = LeadsByStatusQuery.new(account: current_account).call
end
# Aggregation query with presenter
def index
stats_query = DashboardStatsQuery.new(user: current_user)
@stats = DashboardStatsPresenter.new(stats_query)
enduser: or account:)@return).includes() to prevent N+115fdeaf
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.