active-interaction-coder
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseActiveInteraction Coder
ActiveInteraction 代码实现指南
Typed business operations as the structured alternative to service objects.
类型化业务操作:作为服务对象的结构化替代方案。
Why ActiveInteraction Over Service Objects
为什么选择ActiveInteraction而非服务对象
| Service Objects | ActiveInteraction |
|---|---|
| No standard interface | Consistent |
| Manual type checking | Built-in type declarations |
| Manual validation | Standard Rails validations |
| Hard to compose | Native composition |
| Verbose boilerplate | Clean, self-documenting |
| 服务对象 | ActiveInteraction |
|---|---|
| 无标准接口 | 统一的 |
| 手动类型检查 | 内置类型声明 |
| 手动验证 | 标准Rails验证 |
| 难以组合 | 原生支持组合 |
| 冗余样板代码 | 简洁、自文档化 |
Setup
设置
ruby
undefinedruby
undefinedGemfile
Gemfile
gem "active_interaction", "~> 5.3"
undefinedgem "active_interaction", "~> 5.3"
undefinedSimple Interaction
简单交互示例
ruby
undefinedruby
undefinedapp/interactions/users/create.rb
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
endend
end
undefinedmodule 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
endend
end
undefinedRunning Interactions
执行交互
ruby
undefinedruby
undefinedIn controller
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
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)
With bang method (raises on error)
user = Users::Create.run!(email: "user@example.com", name: "John")
undefineduser = Users::Create.run!(email: "user@example.com", name: "John")
undefinedInput Types
输入类型
ruby
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
endruby
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
endComposing Interactions
组合交互
ruby
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
endruby
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
endController Pattern
控制器模式
ruby
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
endruby
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
endTesting Interactions
测试交互
ruby
RSpec.describe Users::Create do
let(:valid_params) { { email: "user@example.com", 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
endruby
RSpec.describe Users::Create do
let(:valid_params) { { email: "user@example.com", 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
endAdvanced Patterns
高级模式
For composition, error handling, and custom types see:
references/active-interaction.md
关于组合、错误处理和自定义类型,请参阅:
references/active-interaction.md