mcpField
B
boxlang-interceptors

boxlang-interceptors

github.com/ortus-boxlang開発043 回インストール1 週間前に更新
リポジトリ

boxlang-interceptors について

BoxLang向けに、横断的な処理を業務コードから分離するイベント駆動型インターセプターの構築方法をまとめたスキルです。登録、イベント処理、実行優先度、非同期通知を扱います。

主な機能

  1. 01名前付きイベントのハンドラー
  2. 02同期・非同期のイベント通知
  3. 03優先度による実行順制御
  4. 04アプリケーション・モジュール登録
  5. 05クラス生成時のライフサイクルイベント

活用シーン

  1. 01操作にログ記録やセキュリティ検査を追加
  2. 02保存前のデータ検証やサニタイズ
  3. 03イベント後のバックグラウンド処理
  4. 04依存性注入やクラス生成の監査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ortus-boxlang/skills --skill boxlang-interceptors

インストール後、Claude Code で/boxlang-interceptorsを入力して読み込まれたか確認します。

詳細

BoxLang インターセプター

BoxLang は、開発者と AI エージェントが連携してアプリケーションを構築、モダナイズ、実行するための、AI ネイティブなソフトウェア生産性プラットフォームです。

概要

BoxLang のインターセプターは、イベント駆動型の通知システムを通じて、アスペクト指向プログラミング(AOP)の機能を提供します。インターセプターは名前付きイベントを監視し、ログ記録、セキュリティ、キャッシュ、検証といった横断的なロジックを実行します。これにより、そのロジックをビジネスコードに結合させずに済みます。優先度で実行順を制御でき、非同期の通知はバックグラウンドスレッドで実行されます。

インターセプターの作成

/**
 * interceptors/RequestLogger.bx
 */
class {

    function onRequestStart( struct interceptData ) {
        request.startTime = getTickCount()
        writeLog( text: "Request started: #cgi.script_name#", type: "information" )
    }

    function onRequestEnd( struct interceptData ) {
        var duration = getTickCount() - request.startTime
        writeLog( text: "Completed in #duration#ms: #cgi.script_name#", type: "information" )
    }
}

インターセプターの登録

Application.bx(アプリケーションスコープ)
class {
    this.name = "MyApp"

    this.interceptors = [
        { class: "interceptors.RequestLogger" },
        { class: "interceptors.SecurityCheck", priority: 1 },
        { class: "interceptors.CacheWarmer",   priority: 10 }
    ]

    function onApplicationStart() {
        announce( "onApplicationStart", { timestamp: now() } )
    }
}
プログラムによる登録(BIF)
// Register at runtime using BoxRegisterInterceptor()
BoxRegisterInterceptor( "interceptors.RequestLogger" )

// With named options
BoxRegisterInterceptor(
    class: "interceptors.SecurityCheck",
    priority: 1,
    properties: { secret: getSetting( "JWT_SECRET" ) }
)

イベントの通知

// Synchronous — all listeners execute before returning
announce( "userCreated", {
    userID: newUser.id,
    email: newUser.email,
    timestamp: now()
} )

// Asynchronous — runs in a background thread
announceAsync( "emailQueued", {
    to: user.email,
    subject: "Welcome"
} )

一般的なインターセプターパターン

操作前/操作後のフック
class DataSanitizer {

    /** Sanitize data before save */
    function preDataSave( interceptData ) {
        var data = interceptData.data

        data.each( ( key, value ) -> {
            if ( isSimpleValue( value ) ) data[key] = trim( value )
        } )

        if ( data.keyExists( "description" ) ) {
            data.description = htmlEditFormat( data.description )
        }
    }

    /** Clear cache after save */
    function postDataSave( interceptData ) {
        cacheRemove( "#interceptData.entityName#_#interceptData.entityID#" )
    }
}
検証ガード
class ValidationInterceptor {

    property name="validationService" inject="ValidationService"

    function preEntitySave( interceptData ) {
        var result = validationService.validate( interceptData.entity )

        if ( result.hasErrors() ) {
            interceptData.abortProcessing = true
            throw(
                type: "ValidationException",
                message: "Validation failed",
                detail: jsonSerialize( result.getAllErrors() )
            )
        }
    }
}
セキュリティガード
class SecurityInterceptor {

    property name="authService" inject="AuthService"

    function preHandler( interceptData ) {
        // Skip public areas
        if ( listFindNoCase( "home,auth", interceptData.handler ) ) return

        if ( !authService.isLoggedIn() ) {
            interceptData.event.overrideEvent( "auth.login" )
            interceptData.abortProcessing = true
        }
    }
}
非同期のバックグラウンド処理
class AsyncProcessor {

    /** Kick off work asynchronously */
    function onEmailQueued( interceptData ) {
        announceAsync( "processEmail", {
            to:      interceptData.to,
            subject: interceptData.subject,
            body:    interceptData.body
        } )
    }

    /** Worker — runs in background thread */
    function onProcessEmail( interceptData ) {
        mailService.send(
            to:      interceptData.to,
            subject: interceptData.subject,
            body:    interceptData.body
        )
    }
}
優先度を使った責任の連鎖
// Lower number = runs earlier
this.interceptors = [
    { class: "interceptors.RequestLogger", priority: 1  },
    { class: "interceptors.AuthCheck",     priority: 2  },
    { class: "interceptors.RateLimiter",   priority: 3  }
]

インターセプターの状態

class RequestTracker {

    variables.requestMap = {}

    function onRequestStart( interceptData ) {
        var id = createUUID()
        variables.requestMap[id] = { startTime: getTickCount(), path: cgi.script_name }
        interceptData.requestID = id
    }

    function onRequestEnd( interceptData ) {
        var id   = interceptData.requestID
        var data = variables.requestMap[id]
        writeLog( "Request #id# done in #getTickCount() - data.startTime#ms" )
        structDelete( variables.requestMap, id )
    }
}

モジュールの登録

モジュールは ModuleConfig.bx でインターセプターを登録します。

// ModuleConfig.bx
class {

    function configure() {
        interceptors = [
            { class: "#moduleMapping#.interceptors.MyInterceptor", priority: 5 }
        ]
    }
}

クラスのインスタンス化イベント(v1.18+)

イベント発生時点データ
afterBoxClassCreationインスタンスが完全に定義された後(疑似コンストラクターの実行、インターフェースおよび抽象メソッドの検証が完了)で、init() の前。createObject() やデシリアライズなどの noInit による生成時にも発生します。extends チェーン内のスーパークラスでは発生しません。instance、className、noInit、context
afterBoxClassInitinit()(または暗黙のコンストラクター)の完了後。noInit による生成時、または init() が例外をスローした場合は発生しません。instance、result、className、context

依存性の注入、監査、可観測性に使用できます。

ベストプラクティス

  • 単一責任 — 1つのインターセプターにつき、1つの関心事を扱います
  • インターセプターは高速に保つ — 時間のかかる処理は announceAsync() に移します
  • ハンドラー内では必ず try/catch を使う — onUserCreated 内で例外がスローされても、ユーザーの作成を妨げないようにします
  • 条件に応じて早期リターンする — メソッドの先頭で、対象外のエンティティやイベントをスキップします
  • 優先度 — セキュリティインターセプターには最小値の優先度 1 を、ログ記録には大きな値を設定します
  • 再帰を避ける — 監視しているイベントと同じイベントを通知しないでください
// ✅ Fast: log then hand off heavy work
function onUserCreated( interceptData ) {
    writeLog( "User created: #interceptData.email#" )
    announceAsync( "sendWelcomeEmail", interceptData )
}

// ✅ Guard with early return
function onDataSave( interceptData ) {
    if ( interceptData.entityName != "User" ) return
    // user-specific logic
}

// ✅ Error-safe side effects
function onUserCreated( interceptData ) {
    try {
        sendEmail( interceptData.email )
    } catch ( any e ) {
        writeLog( "Email error: #e.message#" )
        // Do NOT rethrow — don't break user creation
    }
}

// ❌ Recursion: triggers same interceptor again
function onDataSave( interceptData ) {
    announce( "onDataSave", data )   // infinite loop
}

コメント