Use when controller actions grow beyond simple CRUD, business logic spans multiple models, or operations involve external APIs or multi-step transactions. Also applies when deciding whether to extract a service object, form object, or query object. Covers the Callable concern, Result objects, and file organization patterns.
Extracts complex business logic into service objects for multi-model operations, external APIs, and transactional workflows.
npx claudepluginhub chaserx/cpcThis skill inherits all available tools. When active, it can use any tool Claude has access to.
examples/callable.rbexamples/create_post.rbexamples/create_post_spec.rbexamples/create_post_test.rbexamples/registration_form.rbexamples/transfer_funds.rbreferences/external-api-services.mdreferences/form-objects.mdreferences/query-objects.mdreferences/result-object.mdreferences/transaction-services.mdExtract business logic that spans multiple models or involves external systems into service objects to promote single responsibility, testability, and reusability.
Indicators for service extraction:
Keep logic in the model when:
| Pattern | Use When | Location |
|---|---|---|
| Service Object | Multi-model business logic | app/services/ |
| Transaction Service | Atomic multi-step operations | app/services/ |
| External API Service | Third-party integrations | app/services/<provider>/ |
| Query Object | Complex, reusable queries | app/queries/ |
| Form Object | Multi-model form submissions | app/forms/ |
| Result Object | Consistent service return type | app/services/result.rb |
DRY up the repeated .call class method across services with a shared concern. See examples/callable.rb for the full implementation.
module Callable
extend ActiveSupport::Concern
class_methods do
def call(...)
new(...).call
end
end
end
Include in every service to get MyService.call(args) delegation to new(args).call.
A minimal service with the Callable concern and Result return type:
class CreatePost
include Callable
def initialize(params, user)
@params = params
@user = user
end
def call
post = @user.posts.build(@params)
if post.save
NotifyFollowersJob.perform_later(post.id)
Result.success(post: post)
else
Result.failure(errors: post.errors.full_messages)
end
end
end
See examples/create_post.rb for the full example.
app/
├── services/
│ ├── concerns/
│ │ └── callable.rb
│ ├── result.rb
│ ├── create_post.rb
│ ├── transfer_funds.rb
│ └── github/
│ └── create_issue.rb
├── queries/
│ └── active_users_query.rb
└── forms/
└── registration_form.rb
Test services through their .call interface. Assert on Result success/failure and side effects. Both RSpec and Minitest examples are available in examples/:
examples/create_post_spec.rb — RSpec exampleexamples/create_post_test.rb — Minitest example.call class method with explicit parametersFor detailed implementations, patterns, and alternatives:
references/result-object.md — Full Result implementation, Struct-based alternative, and dry-monads approachreferences/transaction-services.md — Transaction wrapping, nested transactions, and side-effect managementreferences/external-api-services.md — API wrapper pattern with error handling, timeouts, and retry guidancereferences/query-objects.md — Query object pattern, composability, and when to prefer scopesreferences/form-objects.md — Multi-model forms with ActiveModel, validations, and form builder integrationWorking code in examples/:
examples/callable.rb — Callable concern to DRY up .call boilerplateexamples/create_post.rb — Basic service objectexamples/transfer_funds.rb — Transaction serviceexamples/registration_form.rb — Form objectexamples/create_post_spec.rb — RSpec test exampleexamples/create_post_test.rb — Minitest test exampleActivates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.