mit einem Klick
controller
// Create a controller with commands, read model queries, routes, and integration tests
// Create a controller with commands, read model queries, routes, and integration tests
Plan a new feature end-to-end — impact analysis across all layers before delegating to /domain, /read-model, /controller skills
Create atomic git commits following project conventions
Build a new read model following project conventions (event handlers, tests, migration, configuration)
Upgrade Rails framework to a newer version following the smooth upgrade methodology
Upgrade RailsEventStore (RES) gems to a newer version
Create a new domain bounded context with aggregates, commands, events, and handlers
| name | controller |
| description | Create a controller with commands, read model queries, routes, and integration tests |
Use this skill when asked to create a new controller or add actions to an existing controller in any Rails app under apps/.
Determine which app the controller belongs to. Default is apps/rails_application/ unless specified.
Before writing any code, clarify:
admin/, client/)Create a test file at test/integration/{controller_name}_test.rb.
require "test_helper"
class ResourceNameTest < InMemoryRESIntegrationTestCase
def test_list_resources
get "/resources"
assert_response :success
end
def test_create_resource
post "/resources", params: { name: "Test" }
follow_redirect!
assert_select("td", "Test")
end
def test_update_resource
resource_id = create_resource("Original")
patch "/resources/#{resource_id}", params: { name: "Updated" }
follow_redirect!
assert_select("td", "Updated")
end
private
def create_resource(name)
post "/resources", params: { name: name }
follow_redirect!
# extract ID from page or use read model facade
ReadModel.all.last.uid
end
end
Integration test conventions:
InMemoryRESIntegrationTestCaseget, post, patch, deletefollow_redirect! after redirecting actionsassert_response, assert_selectAdd to config/routes.rb:
resources :resources, only: [:index, :show, :new, :create, :edit, :update, :destroy] do
member do
post :custom_action
end
collection do
delete :clear_all
end
end
Route conventions:
resources with only: to limit actionsmember do (operates on one record) or collection do (operates on many)namespace :admin do
resources :stores, only: [:index, :new, :create]
end
class ResourcesController < ApplicationController
def index
@resources = ReadModel.all
end
def show
@resource = ReadModel.find_by_uid(params[:id])
end
def new
@resource_id = SecureRandom.uuid
end
def create
ActiveRecord::Base.transaction do
command_bus.call(Domain::CreateResource.new(
resource_id: params[:resource_id],
name: params[:name]
))
end
redirect_to resources_path, notice: "Resource was successfully created"
end
def edit
@resource = ReadModel.find_by_uid(params[:id])
end
def update
command_bus.call(Domain::UpdateResource.new(
resource_id: params[:id],
name: params[:name]
))
redirect_to resource_path(params[:id]), notice: "Resource was successfully updated"
end
def destroy
command_bus.call(Domain::RemoveResource.new(resource_id: params[:id]))
redirect_to resources_path, notice: "Resource was successfully removed"
end
end
Command dispatch:
command_bus.call(Command.new(...)) to dispatch commandsActiveRecord::Base.transaction do ... endid = SecureRandom.uuidRead model queries:
ReadModel.all, ReadModel.find_by_uid(id)Redirects:
redirect_to path, notice: "..."redirect_to path, alert: "..."Error handling:
rescue/else for domain exceptions:
def submit
SomeService.new(params[:id]).call
rescue Domain::SomeError => e
redirect_to edit_path(params[:id]), alert: e.message
else
redirect_to show_path(params[:id]), notice: "Success"
end
Before actions:
before_action :load_resource, only: [:show, :edit, :update]
private
def load_resource
@resource = ReadModel.find_by_uid(params[:id])
end
For non-trivial forms, create a form object in the controller file or a separate file:
class ResourceForm
include ActiveModel::Model
include ActiveModel::Attributes
include ActiveModel::Validations
attribute :name, :string
attribute :price, :decimal
attribute :resource_id, :string
validates :name, presence: true
validates :price, presence: true, numericality: { greater_than: 0 }
end
Use in the controller:
def create
form = ResourceForm.new(**resource_params)
unless form.valid?
return render "new", locals: { errors: form.errors }, status: :unprocessable_entity
end
ActiveRecord::Base.transaction do
command_bus.call(Domain::CreateResource.new(resource_id: form.resource_id, name: form.name))
end
redirect_to resources_path, notice: "Created"
end
When a controller action involves complex coordination (multiple commands, event subscriptions, error handling), extract to a service:
# app/services/{namespace}/submit_service.rb
module Namespace
class SubmitService
def initialize(id)
@id = id
end
def call
ActiveRecord::Base.transaction do
command_bus.call(Domain::Submit.new(id: @id))
end
end
private
def command_bus
Rails.configuration.command_bus
end
end
end
For controllers under a namespace (e.g. admin/, client/):
# app/controllers/admin/resources_controller.rb
module Admin
class ResourcesController < ApplicationController
def index
@resources = ReadModel.all
end
end
end
With a base controller for shared behavior:
# app/controllers/client/base_controller.rb
module Client
class BaseController < ApplicationController
layout "client_panel"
before_action :ensure_logged_in
private
def ensure_logged_in
# auth check
end
end
end
# app/controllers/client/orders_controller.rb
module Client
class OrdersController < BaseController
def index
@orders = ClientOrders.orders_for_client(current_client_id)
end
end
end
Create ERB views in app/views/{controller_name}/:
index.html.erb — list viewshow.html.erb — detail viewnew.html.erb — form for creatingedit.html.erb — form for editingViews query data via instance variables set in controller (@resources, @resource).
rails test test/integration/{test_file}.rb — integration tests passmake test — all tests green