彻底掌握ActiveInteraction:构建Ruby优雅业务逻辑的完整指南

彻底掌握ActiveInteraction:构建Ruby优雅业务逻辑的完整指南

【免费下载链接】active_interaction :briefcase: Manage application specific business logic. 【免费下载链接】active_interaction 项目地址: https://gitcode.com/gh_mirrors/ac/active_interaction

为什么业务逻辑需要ActiveInteraction?

你是否还在为Ruby项目中散落的业务逻辑感到困扰?模型中充斥着复杂的before_save回调,控制器里塞满了条件判断,测试变得举步维艰?ActiveInteraction作为Ruby生态中领先的服务对象(Service Object)实现,提供了一种优雅的方式来封装业务逻辑,让代码更具可读性、可测试性和可维护性。

读完本文,你将能够:

  • 理解ActiveInteraction如何解决传统Rails应用的业务逻辑混乱问题
  • 掌握核心概念与过滤器系统的全面应用
  • 实现与Rails框架的无缝集成
  • 运用高级特性构建复杂业务流程
  • 遵循经过验证的最佳实践与设计模式

![ActiveInteraction架构](https://mermaid.ink/img/pako:eNqNk11v2jAUhv_KkK1UK1QdZg442a00g2Y2bJaDSLWlrSdtSpTkdSHP--SVbtSpUq2d2d2Z2ZkHkCgH2YgQn3q3TjE3sY2y5QjDUUJFO-0M01gH5U0cZg1Iqg1m4J1w9Y4LrQ2x1l8QZ40n9H0J8Qr1O9Xr6hX2l4t9A9T033Jf0Wf0VvUbT0Jz9Kf0Yr0KP0GX0Wv0XfUbD0JL0HX0Wv0XPOBL0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBH0Kf0Yr0KP0GX0Wv0XPOBL0Kf0Yr0KP

一、ActiveInteraction简介

ActiveInteraction是一个专为Ruby应用设计的业务逻辑管理库,遵循服务对象(Service Object)模式,特别适合与Rails框架无缝集成。它通过提供结构化的输入验证和业务逻辑封装,解决了传统Rails应用中业务逻辑分散在模型和控制器中的问题。

1.1 核心价值

  • 关注点分离:将业务逻辑从模型和控制器中抽离,形成独立可测试的交互对象
  • 类型安全:强大的输入过滤系统确保数据类型正确
  • 验证集成:与ActiveModel验证无缝集成,提供一致的错误处理机制
  • 代码复用:通过组合多个交互实现复杂业务流程
  • 可测试性:每个交互都是独立单元,易于编写隔离测试

1.2 与其他模式的对比

模式优势劣势适用场景
ActiveInteraction类型安全、验证集成、组合能力额外抽象层中等复杂度业务逻辑
模型回调简单直接隐式依赖、难以调试简单数据操作
控制器逻辑快速开发职责混乱、难以测试极简单应用
普通服务对象灵活自由缺乏标准、重复代码特定定制场景

二、安装与基础配置

2.1 安装步骤

# 在Gemfile中添加
gem 'active_interaction', '~> 5.5'

# 执行安装
bundle install

或手动安装:

gem install active_interaction --version '~> 5.5'

2.2 项目结构

推荐在Rails应用中创建app/interactions目录组织交互对象,并按领域模型分组:

app/
├── interactions/
│   ├── accounts/
│   │   ├── create_account.rb
│   │   ├── update_account.rb
│   │   └── delete_account.rb
│   └── orders/
│       ├── create_order.rb
│       └── process_payment.rb

三、核心概念与基本用法

3.1 交互对象的基本结构

每个交互对象都是ActiveInteraction::Base的子类,包含两个核心部分:输入定义业务逻辑

# app/interactions/math/square.rb
class Math::Square < ActiveInteraction::Base
  # 定义输入 - 浮点数x
  float :x

  # 定义业务逻辑
  def execute
    x**2  # 返回计算结果
  end
end

3.2 执行交互

使用.run方法执行交互,返回一个结果对象:

# 执行交互
outcome = Math::Square.run(x: 2.1)

# 检查结果
if outcome.valid?
  puts "结果: #{outcome.result}"  # 输出: 结果: 4.41
else
  puts "错误: #{outcome.errors.full_messages.join(', ')}"
end

或者使用.run!方法直接获取结果(失败时抛出异常):

begin
  result = Math::Square.run!(x: 2.1)
  puts "结果: #{result}"  # 输出: 结果: 4.41
rescue ActiveInteraction::InvalidInteractionError => e
  puts "错误: #{e.message}"
end

3.3 交互生命周期

mermaid

四、输入过滤系统详解

ActiveInteraction提供了丰富的输入过滤类型,确保输入数据符合预期格式。

4.1 基本过滤器类型

过滤器类型用途示例
boolean布尔值boolean :active, default: true
string字符串string :name, strip: false
symbol符号symbol :status
integer整数integer :age, default: 0
float浮点数float :score
decimal高精度小数decimal :price, digits: 2
date日期date :birthday
date_time日期时间date_time :start_at
time时间time :alarm_time
array数组array :tags
hash哈希hash :options
file文件上传file :avatar

4.2 过滤器选项

所有过滤器都支持一些通用选项:

# 可选输入(默认值为nil)
string :middle_name, default: nil

# 带描述的输入(用于文档生成)
integer :quantity, desc: '商品数量'

# 数组过滤器与子过滤器
array :scores do
  float  # 数组元素必须是浮点数
end

# 哈希过滤器与嵌套过滤器
hash :user do
  string :name
  integer :age
end

4.3 高级过滤器

4.3.1 Object过滤器

确保输入是指定类的实例:

class UserInteraction < ActiveInteraction::Base
  # 要求输入是User类实例
  object :user, class: User
  
  def execute
    user.update(last_login: Time.now)
    user
  end
end
4.3.2 Record过滤器

自动查找ActiveRecord对象:

class FindPost < ActiveInteraction::Base
  # 自动通过ID查找Post
  record :post
  
  def execute
    post
  end
end

# 可以直接传入ID
outcome = FindPost.run(post: 123)
# 或者传入对象
outcome = FindPost.run(post: Post.first)
4.3.3 Interface过滤器

验证输入是否实现了特定接口:

class DataExporter < ActiveInteraction::Base
  # 要求输入对象实现dump和load方法
  interface :serializer, methods: [:dump, :load]
  
  def execute
    serializer.dump(data)
  end
end

五、验证系统

ActiveInteraction与ActiveModel验证系统无缝集成,提供多种验证方式。

5.1 基本验证

class CreateUser < ActiveInteraction::Base
  string :email
  string :password
  
  # 基本验证
  validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
  validates :password, length: { minimum: 8 }
  
  def execute
    User.create!(email: email, password: password)
  end
end

5.2 条件验证

class UpdateProfile < ActiveInteraction::Base
  object :user
  string :name, default: nil
  string :email, default: nil
  
  # 仅当提供了name时验证
  validates :name, presence: true, unless: -> { name.nil? }
  # 仅当提供了email时验证格式
  validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }, unless: -> { email.nil? }
  
  def execute
    user.update!(name: name, email: email) if name || email
    user
  end
end

5.3 自定义验证方法

class TransferFunds < ActiveInteraction::Base
  integer :amount
  object :from_account
  object :to_account
  
  validate :sufficient_funds
  
  def execute
    from_account.decrement(:balance, amount)
    to_account.increment(:balance, amount)
    [from_account, to_account]
  end
  
  private
  
  # 自定义验证方法
  def sufficient_funds
    return if from_account.balance >= amount
    errors.add(:amount, '余额不足')
  end
end

六、Rails集成实战

6.1 控制器中的使用

将业务逻辑移至交互对象,保持控制器精简:

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def create
    outcome = CreatePost.run(post_params)
    
    if outcome.valid?
      redirect_to outcome.result, notice: '文章创建成功'
    else
      @post = outcome
      render :new
    end
  end
  
  private
  
  # 不再需要强参数,交互会自动过滤未定义的输入
  def post_params
    params.require(:post).permit(:title, :content)
  end
end

# app/interactions/create_post.rb
class CreatePost < ActiveInteraction::Base
  string :title
  string :content
  
  validates :title, presence: true, length: { in: 3..100 }
  validates :content, presence: true
  
  def execute
    Post.create!(title: title, content: content)
  end
end

6.2 资源型控制器完整示例

使用交互重构RESTful控制器:

# app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
  def index
    @accounts = ListAccounts.run!.result
  end
  
  def show
    @account = find_account
  end
  
  def new
    @account = CreateAccount.new
  end
  
  def create
    outcome = CreateAccount.run(account_params)
    
    if outcome.valid?
      redirect_to outcome.result, notice: '账户创建成功'
    else
      @account = outcome
      render :new
    end
  end
  
  def edit
    @account = UpdateAccount.new(find_account.attributes)
  end
  
  def update
    outcome = UpdateAccount.run(account_params.merge(account: find_account))
    
    if outcome.valid?
      redirect_to outcome.result, notice: '账户更新成功'
    else
      @account = outcome
      render :edit
    end
  end
  
  def destroy
    DestroyAccount.run!(account: find_account)
    redirect_to accounts_url, notice: '账户已删除'
  end
  
  private
  
  def find_account
    FindAccount.run!(id: params[:id]).result
  end
  
  def account_params
    params.fetch(:account, {})
  end
end

6.3 视图集成

交互对象实现了ActiveModel接口,可以直接在表单中使用:

# app/views/accounts/new.html.erb
<%= form_with model: @account, url: accounts_path do |form| %>
  <% if @account.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@account.errors.count, "error") %> prohibited this account from being saved:</h2>
      <ul>
        <% @account.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name %>
  </div>

  <div class="field">
    <%= form.label :email %>
    <%= form.email_field :email %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

七、高级特性

7.1 交互组合

使用compose方法在一个交互中调用另一个交互,实现逻辑复用:

# 加法交互
class Add < ActiveInteraction::Base
  float :x, :y
  
  def execute
    x + y
  end
end

# 组合交互
class AddAndDouble < ActiveInteraction::Base
  float :a, :b
  
  def execute
    # 组合Add交互
    sum = compose(Add, x: a, y: b)
    sum * 2  # 将结果翻倍
  end
end

# 执行组合交互
outcome = AddAndDouble.run(a: 1, b: 2)
puts outcome.result  # 输出: 6.0

7.2 回调系统

利用回调钩子在交互生命周期的不同阶段执行代码:

class TrackedInteraction < ActiveInteraction::Base
  string :action
  
  # 定义回调
  set_callback :execute, :before, :log_start
  set_callback :execute, :after, :log_complete
  
  def execute
    # 业务逻辑
    puts "执行 #{action}"
  end
  
  private
  
  def log_start
    @start_time = Time.now
    Rails.logger.info "交互开始: #{action}"
  end
  
  def log_complete
    duration = Time.now - @start_time
    Rails.logger.info "交互完成: #{action}, 耗时: #{duration}秒"
  end
end

7.3 输入继承与导入

通过import_filters导入其他交互的输入定义:

class UserBase < ActiveInteraction::Base
  string :name
  string :email
end

class CreateUser < ActiveInteraction::Base
  # 导入UserBase的所有输入
  import_filters UserBase
  
  string :password
  
  def execute
    User.create!(name: name, email: email, password: password)
  end
end

class UpdateUser < ActiveInteraction::Base
  # 导入UserBase的输入,但排除email
  import_filters UserBase, except: [:email]
  
  object :user
  
  def execute
    user.update!(name: name)
    user
  end
end

八、最佳实践与设计模式

8.1 命名约定

  • 使用名词+动词的命名方式:CreateUserUpdateOrder
  • 按领域模型分组交互:User::CreateUser::UpdateOrder::Process
  • 使用一致的结果返回:成功返回业务对象,失败通过errors传达

8.2 错误处理策略

class SafeInteraction < ActiveInteraction::Base
  def execute
    # 使用事务确保数据一致性
    ActiveRecord::Base.transaction do
      # 业务逻辑
      result = risky_operation
      
      # 手动添加错误
      if result.invalid?
        errors.add(:base, '操作失败')
        errors.merge!(result.errors)  # 合并其他对象的错误
        return  # 退出执行
      end
      
      result
    end
  end
end

8.3 测试策略

每个交互都是独立单元,易于编写测试:

【免费下载链接】active_interaction :briefcase: Manage application specific business logic. 【免费下载链接】active_interaction 项目地址: https://gitcode.com/gh_mirrors/ac/active_interaction

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值