plugins/lisa-rails-copilot/skills/active-record-model-best-practices/SKILL.md
--- name: active-record-model-best-practices description: Best practices for Ruby on Rails models, splitting code into well-organized, maintainable code. Use when a model exceeds ~100 lines, has mixed responsibilities, or when the user asks to refactor, extract, clean up, or organize a Rails model. Applies patterns: concerns, service objects, query objects, form objects, and value objects. --- # Rails Model Refactoring When refactoring a Rails model, analyze the file and extract code into the
npx skillsauth add codyswanngt/lisa plugins/lisa-rails-copilot/skills/active-record-model-best-practicesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
When refactoring a Rails model, analyze the file and extract code into the appropriate pattern based on what the code does. The model itself should only contain associations, enums, basic validations, and concern includes.
Read the model file and classify each block of code:
| Code type | Extract to | Location |
|---|---|---|
| Related scopes + simple methods sharing a theme | Concern | app/models/concerns/ |
| Business logic, multi-step operations, callbacks with side effects | Service object | app/services/ |
| Complex queries, multi-join scopes, reporting queries | Query object | app/queries/ |
| Context-specific validations (e.g. registration vs admin update) | Form object | app/forms/ |
| Domain concepts beyond a primitive (money, coordinates, scores) | Value object | app/models/ |
| Associations, enums, core validations, simple scopes | Keep on model | — |
Use for grouping related scopes, validations, callbacks, and simple instance methods that share a single theme. Name the concern after the capability it provides.
# app/models/concerns/searchable.rb
module Searchable
extend ActiveSupport::Concern
included do
scope :search, ->(query) { where("name ILIKE ?", "%#{query}%") }
end
def matching_terms(query)
name.scan(/#{Regexp.escape(query)}/i)
end
end
Use for business logic, orchestration of multiple models, and anything triggered by a user action that involves more than a simple CRUD operation. Follow the single-responsibility principle — one service, one operation.
# app/services/players/calculate_stats.rb
module Players
class CalculateStats
def initialize(player)
@player = player
end
def call
# complex logic here
end
end
end
Conventions:
Players::CalculateStatscallinitializeUse for complex database queries that involve joins, subqueries, CTEs, or multi-condition filtering that would clutter a model with scopes.
# app/queries/players/free_agent_query.rb
module Players
class FreeAgentQuery
def initialize(relation = Player.all)
@relation = relation
end
def call(filters = {})
@relation
.where(contract_status: :expired)
.where("age < ?", filters[:max_age])
.joins(:stats)
.order(war: :desc)
end
end
end
Conventions:
initialize (default to Model.all)callUse when validations only apply in specific contexts, or when a form spans multiple models.
# app/forms/player_registration_form.rb
class PlayerRegistrationForm
include ActiveModel::Model
include ActiveModel::Attributes
attribute :name, :string
attribute :email, :string
attribute :team_id, :integer
attribute :position, :string
validates :name, :email, :position, presence: true
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
def save
return false unless valid?
Player.create!(attributes)
end
end
Use for domain concepts that deserve their own identity beyond a raw primitive.
# app/models/batting_average.rb
class BattingAverage
include Comparable
def initialize(hits, at_bats)
@hits = hits
@at_bats = at_bats
end
def value
return 0.0 if @at_bats.zero?
(@hits.to_f / @at_bats).round(3)
end
def elite?
value >= 0.300
end
def <=>(other)
value <=> other.value
end
end
tools
--- name: harper-realtime description: This skill should be used when adding or troubleshooting Harper (HarperDB/Fabric) real-time behavior: MQTT topics, WebSocket resource subscriptions, resource publish/subscribe handlers, SSE-style streaming routes, and local subscriber verification. Pairs with harper-resources, harper-config-yaml, harper-schema-graphql, and harper-build-and-deploy. --- # Harper Realtime ## Overview Harper exposes live data through the same Resource model used for REST and
tools
--- name: harper-realtime description: This skill should be used when adding or troubleshooting Harper (HarperDB/Fabric) real-time behavior: MQTT topics, WebSocket resource subscriptions, resource publish/subscribe handlers, SSE-style streaming routes, and local subscriber verification. Pairs with harper-resources, harper-config-yaml, harper-schema-graphql, and harper-build-and-deploy. --- # Harper Realtime ## Overview Harper exposes live data through the same Resource model used for REST and
tools
--- name: harper-realtime description: This skill should be used when adding or troubleshooting Harper (HarperDB/Fabric) real-time behavior: MQTT topics, WebSocket resource subscriptions, resource publish/subscribe handlers, SSE-style streaming routes, and local subscriber verification. Pairs with harper-resources, harper-config-yaml, harper-schema-graphql, and harper-build-and-deploy. --- # Harper Realtime ## Overview Harper exposes live data through the same Resource model used for REST and
tools
--- name: harper-realtime description: This skill should be used when adding or troubleshooting Harper (HarperDB/Fabric) real-time behavior: MQTT topics, WebSocket resource subscriptions, resource publish/subscribe handlers, SSE-style streaming routes, and local subscriber verification. Pairs with harper-resources, harper-config-yaml, harper-schema-graphql, and harper-build-and-deploy. --- # Harper Realtime ## Overview Harper exposes live data through the same Resource model used for REST and