mcpField
A
active-interaction-coder

active-interaction-coder

github.com/majesticlabs-devDevelopment5062 installsUpdated 7w ago
Repository

About active-interaction-coder

A coding skill for structuring Rails business operations as typed ActiveInteraction classes. It covers input declarations, validation, composition, execution through `.run` or `.run!`, and testing.

Key features

  1. 01Typed operation inputs
  2. 02Standard Rails validation
  3. 03Composable interactions
  4. 04`.run` and `.run!` execution
  5. 05Interaction testing patterns

Use cases

  1. 01Implementing a user-registration operation
  2. 02Composing multiple business operations
  3. 03Testing operations with valid inputs

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/majesticlabs-dev/majestic-marketplace --skill active-interaction-coder

After installing, type/active-interaction-coderin Claude Code to confirm it loaded.

Details

From the project README

ActiveInteraction Coder

Typed business operations as the structured alternative to service objects.

Why ActiveInteraction Over Service Objects

Service ObjectsActiveInteraction
No standard interfaceConsistent .run / .run!
Manual type checkingBuilt-in type declarations
Manual validationStandard Rails validations
Hard to composeNative composition
Verbose boilerplateClean, self-documenting

Setup

# Gemfile
gem "active_interaction", "~> 5.3"

Simple Interaction

# app/interactions/users/create.rb
module Users
  class Create < ActiveInteraction::Base
    # Typed inputs
    string :email
    string :name
    string :password, default: nil

    # Validations
    validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
    validates :name, presence: true

    # Main logic
    def execute
      user = User.create!(
        email: email,
        name: name,
        password: password || SecureRandom.alphanumeric(32)
      )

      UserMailer.welcome(user).deliver_later
      user  # Return value becomes outcome.result
    end
  end
end

Running Interactions

# In controller
def create
  outcome = Users::Create.run(
    email: params[:email],
    name: params[:name]
  )

  if outcome.valid?
    redirect_to outcome.result, notice: "User created"
  else
    @errors = outcome.errors
    render :new, status: :unprocessable_entity
  end
end

# With bang method (raises on error)
user = Users::Create.run!(email: "[email protected]", name: "John")

Input Types

class MyInteraction < ActiveInteraction::Base
  # Primitives
  string :name
  integer :age
  float :price
  boolean :active
  symbol :status

  # Date/Time
  date :birthday
  time :created_at
  date_time :scheduled_at

  # Complex types
  array :tags
  hash :metadata

  # Model instances
  object :user, class: User

  # Typed arrays
  array :emails, default: [] do
    string
  end

  # Optional with default
  string :optional_field, default: nil
  integer :count, default: 0
end

Composing Interactions

module Users
  class Register < ActiveInteraction::Base
    string :email, :name, :password

    def execute
      # Compose calls another interaction
      user = compose(Users::Create,
        email: email,
        name: name,
        password: password
      )

      # Errors automatically merged if nested fails
      compose(Users::SendWelcomeEmail, user: user)
      user
    end
  end
end

Controller Pattern

class ArticlesController < ApplicationController
  def create
    outcome = Articles::Create.run(
      title: params[:article][:title],
      body: params[:article][:body],
      author: current_user
    )

    if outcome.valid?
      redirect_to article_path(outcome.result), notice: "Article created"
    else
      @article = Article.new(article_params)
      @article.errors.merge!(outcome.errors)
      render :new, status: :unprocessable_entity
    end
  end
end

Testing Interactions

RSpec.describe Users::Create do
  let(:valid_params) { { email: "[email protected]", name: "John" } }

  it "creates user with valid inputs" do
    expect { described_class.run(valid_params) }
      .to change(User, :count).by(1)
  end

  it "returns valid outcome" do
    outcome = described_class.run(valid_params)
    expect(outcome).to be_valid
    expect(outcome.result).to be_a(User)
  end

  it "validates email format" do
    outcome = described_class.run(valid_params.merge(email: "invalid"))
    expect(outcome).not_to be_valid
    expect(outcome.errors[:email]).to be_present
  end
end

Advanced Patterns

For composition, error handling, and custom types see:

  • references/active-interaction.md

Comments

Markdown supported