第3章:Expression文法編 - runnの真の力を解き放て!

🚀 expr-lang/expr - 最強の式評価エンジンとの出会い

ついに来た! runnの心臓部、expr-lang/exprの世界へようこそ!これは単なる式評価エンジンじゃない。テストシナリオに魔法をかける最強の相棒だ!

なぜexpr-lang/exprが最高なのか?

  • ⚡ Go風の構文: Goプログラマーなら5秒で理解できる直感的な文法!
  • 🛡️ 型安全: 実行時エラー?そんなものは過去の話だ!
  • 🏃‍♂️ 爆速実行: コンパイル済み式でミリ秒単位の処理を実現!
  • 🔒 完全サンドボックス: 安全な実行環境で何も心配いらない

💪 基本的な式の構文 - これさえ覚えれば無敵!

🎯 リテラルと演算子 - あらゆるデータ型を自在に操れ!

expr-lang/exprでは、さまざまなリテラルと演算子を使って、データを表現することができる。以下の表は、リテラルの種類と例を示している。これらを駆使すれば、複雑なデータ構造も簡単に扱えるようになるよ! 詳しくは expr-lang Literals(リテラル) を参考にしてね!

種類 説明
コメント // コメント/* コメント */ 単一行コメント
ブール値 truefalse 真偽値
整数 42-37, 0x2A, 0o52, 0b101010 整数値
浮動小数点数 0.5.5 小数値
文字列 "Hello"'World' 文字列(単一または二重引用符)
配列 [1, 2, 3] 配列・リスト
マップ {a: 1, b: 2, c: 3} 連想配列・オブジェクト
nil nil null値
steps:
  literals_demo:
    test: |
      // 数値
      42 == 42 &&
      3.14 < 4 &&

      // 文字列
      "hello" + " world" == 'hello world' &&

      // ブール値
      true && !false &&

      // 配列
      [1, 2, 3][0] == 1 &&
      len([1, 2, 3]) == 3 &&

      // マップ
      {"name": "alice", "age": 30}.name == "alice"

⚖️ 比較演算子 - 真偽を見極める審判の目!

steps:
  comparison_demo:
    test: |
      // 基本的な比較
      10 > 5 &&
      "apple" < "banana" &&
      100 >= 100 &&
      50 <= 100 &&

      // 等価性
      "test" == "test" &&
      100 != 99 &&

      // 包含チェック
      "running" contains "run" &&
      2 in [1, 2, 3] &&
      "key" in {"key": "value"}

🔥 変数参照の詳細 - データの海を自由に泳げ!

📊 利用可能な変数一覧 - 7つの強力な武器

変数名 スコープ 説明
vars グローバル Runbookで定義された変数
env グローバル 環境変数
steps グローバル すべてのステップの結果
current ステップ内 現在のステップの結果
previous ステップ内 直前のステップの結果
i ループ内 ループのインデックス
parent Include内 親Runbookの変数

💡 変数アクセスの実践例 - これが本物のパワーだ!

desc: 変数参照の包括的な例

vars:
  baseURL: https://api.example.com
  users:
    - id: 1
      name: Alice
    - id: 2
      name: Bob

runners:
  blog: http://localhost:8080

steps:
  # varsへのアクセス
  access_vars:
    dump: |
      {
        "url": vars.baseURL,
        "firstUser": vars.users[0].name,
        "userCount": len(vars.users)
      }

  # 環境変数へのアクセス
  access_env:
    test: |
      env.HOME != "" &&
      env.USER != ""

  # ステップ結果へのアクセス(マップ形式)
  make_request:
    blog:
      /users:
        get: {}
    test: current.res.status == 200
    dump: current.res.body

  # 前のステップの結果を参照
  use_previous:
    test: |
      previous.res.status == 200 &&
      len(steps.make_request.res.body) >= 0

🎨 高度な式パターン - プロフェッショナルへの道

🔀 条件式(三項演算子) - スマートな分岐処理の極意!

steps:
  conditional_expr:
    dump: |
      // 三項演算子
      vars.environment == "prod" ? "https://api.example.com" : "http://localhost:8080"

🔍 フィルタリングとマッピング - データ操作の魔術師になれ!

vars:
  products:
    - name: "iPhone"
      price: 999
      category: "electronics"
    - name: "Book"
      price: 20
      category: "books"
    - name: "MacBook"
      price: 1999
      category: "electronics"
steps:
  filter_example:
    dump: |
      // 価格が100以上の商品をフィルタ
      filter(vars.products, {.price >= 100})

    test: |
      // カテゴリが"electronics"の商品数をカウント
      len(filter(vars.products, {.category == "electronics"})) == 2

  map_example:
    dump: |
      // 商品名のリストを作成
      map(vars.products, {.name})

    test: |
      // すべての商品の価格が0より大きいことを確認
      all(vars.products, {.price > 0})

📦 配列・マップ操作 - コレクションを思いのままに!

vars:
  numbers: [1, 2, 3, 4, 5]
  person:
    name: "Alice"
    skills:
      - "Go"
      - "Python"
      - "JavaScript"

steps:
  array_operations:
    test: |
      // スライス操作
      vars.numbers[1:3] == [2, 3] &&
      vars.numbers[:2] == [1, 2] &&
      vars.numbers[3:] == [4, 5] &&

      // 要素の存在確認
      3 in vars.numbers &&
      !(10 in vars.numbers)

      // 配列の結合(TODO: これは動かない)
      // vars.numbers + [6, 7] == [1, 2, 3, 4, 5, 6, 7]

  map_operations:
    test: |
      // ネストしたアクセス
      vars.person.skills[0] == "Go" &&
      len(vars.person.skills) == 3 &&

      // キーの存在確認
      "name" in vars.person &&
      !("age" in vars.person)

💼 実践的な式の例 - 現場で使える最強テクニック!

🎯 APIレスポンスの検証 - 完璧な検証の極意

desc: 複雑なAPIレスポンスの検証

runners:
  blog: http://localhost:8080

steps:
  add_user:
    blog:
      /users:
        post:
          body:
            application/json:
              name: "John Doe"
              email: "john@example.com"

  get_users:
    blog:
      /users:
        get:
          query:
            page: 1
            limit: 10
    test: |
      // ステータスコードの確認
      current.res.status == 200 &&

      // レスポンスボディの構造確認
      // "data" in current.res.body &&
      // "pagination" in current.res.body &&

      // データの検証
      len(current.res.body) <= 10
      // claude が以下のようなコードを生成するが実際には動かない
      // all(current.res.body.data, {
      //   "id" in . &&
      //   "email" in . &&
      //   .id > 0
      // }) &&

      // ページネーションの検証
      // current.res.body.pagination.page == 1 &&
      // current.res.body.pagination.limit == 10

🔧 デバッグのテクニック - 問題解決のマスターになる!

🔍 dump機能の活用 - すべてを可視化せよ!

TBD

⚠️ よくあるパターンと落とし穴 - 達人への必修科目!

1. 💀 null/undefinedの扱い - 空の罠を回避せよ!

steps:
  null_handling:
    test: |
      # nullチェック
      current.res.body.optional_field != null &&

      # デフォルト値の設定
      (current.res.body.optional_field ?? "default") != "default" &&

      # ネストしたnullチェック
      current.res.body.user?.profile?.bio != null

2. 🔄 型変換 - データ型の壁を打ち破れ!

steps:
  type_conversion:
    test: |
      # 文字列から数値への変換は自動では行われない
      current.res.body.count == "10" &&  # 文字列として比較
      int(current.res.body.count) == 10  # 数値として比較

3. 🚧 配列の境界チェック - 安全第一の鉄則!

steps:
  safe_array_access:
    test: |
      # 配列が空でないことを確認してからアクセス
      len(current.res.body.items) > 0 &&
      current.res.body.items[0].name == "test"

🎊 まとめ - Expression文法マスターへの道

おめでとう! あなたは今、runnの式評価エンジンの達人への第一歩を踏み出した!

🏆 この章で手に入れた5つの武器:

  1. ⚡ 基本的な構文: リテラル、演算子、比較 - 基礎こそが最強の土台
  2. 🔑 変数参照: vars、steps、current、previousなど - データへの完全アクセス権
  3. 🎯 高度なパターン: フィルタリング、マッピング、条件式 - プロ級のテクニック
  4. 💪 実践的な使用例: APIレスポンスの検証、動的リクエスト構築 - 現場で即戦力
  5. 🔧 デバッグテクニック: dump機能の活用、段階的な構築 - 問題解決の秘訣

expr-lang/exprの強力な機能により、どんなに複雑なテストシナリオもエレガントに記述できる。でも、これはまだ序章に過ぎない!