LoginSignup
17
10

More than 5 years have passed since last update.

gyb (Generate Your Boilerplate) によるテンプレート作成

Posted at

swiftの内部で使われているgybを使うことで無駄なコピペコードを減らそうという話。

  1. gybの取得
  2. configファイルの作成
  3. コード生成

gybの取得

pull_gyb.sh
mkdir gyb
curl "https://raw.githubusercontent.com/apple/swift/master/utils/gyb.py" -o "gyb/gyb.py"
curl "https://raw.githubusercontent.com/apple/swift/master/utils/gyb" -o "gyb/gyb"
chmod +x gyb/gyb

取得し実行権限を与えます。

configファイルの作成

今回はLensのSwift実装Lを用いて、手持ちのモデルにLensextensionするコードを生成することにします。

Extension.swift.gybLensExtensions.gyb.pyの2つのファイルを準備します。Extension.swift.gybの方はどういうテンプレートなのかを具体的に示すファイルで、LensExtensions.gyb.pyの方は手持ちのコードで定義してる型情報からどれをテンプレートに使うのかを表したものです。LensExtensions.gyb.pyから情報を引っ張ってきてExtension.swift.gybでテンプレートを作るイメージ。

まず手持ちのモデルとして、以下のようなものがあるとします。

model.swift
public struct Street {
    var name: String
}

この型にLensを追加するとして、LensExtensions.gyb.pyを以下のように作ります。

LensExtensions.gyb.py
lenses_info = {
    "Street": {
        "name": {"type": "String"}
    }
}

型名Streetとプロパティ名nameとその型Stringです。対象のモデルが増えてきたらlenses_infoに追加してやればOK。

次にExtensions.swift.gybを準備します。

Extensions.swift.gyb
%{
import os
import sys

try:
    config
except NameError:
    sys.exit("`-Dconfig` is not specified.")

config_path = os.path.abspath(config)
if not os.path.exists(config_path):
    sys.exit(config_path + " was not found.")
execfile(config_path)

try:
    lenses_info
except NameError:
    sys.exit("no info of lens.")

}%

import L

% for extending_type_name, properties in lenses_info.items():
extension ${extending_type_name} {

% for (key, attributes) in properties.items():
    public var ${key}_lens: Lens<${extending_type_name}, ${attributes["type"]}> {
        return Lens<${extending_type_name}, ${attributes["type"]}>(
            get: { $0.${key} },
            set: { new_${key}, obj in 
                var newObj = obj
                newObj.${key} = new_${key}
                return obj
            }
        )
    }
% end
}

% end

%{から}%で囲われた部分や%で始まる部分、${PARAM}の部分はコード部分でありテンプレートとして出力されません。テンプレートとして出力されるのはそれ以外の部分です。

%{から}%で囲われた部分でパラメータlenses_infoの準備をしています。引数からLensExtensions.gyb.pyへのパスを与えるようにしているため、まずはファイル取得ができているかチェックしています。また、ファイル中にlenses_infoがあるかをチェックしています。

%で始まる部分や${PARAM}の部分はコード部分です。lenses_infoパラメータを使ってforループ回しているだけですので、最後に記す出力ファイルと比較して見てもらえればイメージできるかと思います。

コード生成

./gyb/gyb Extension.swift.gyb -o output.swift -Dconfig=path/to/LensExtensions.gyb.py

output.swiftはテンプレートとして出力されるファイル。-D[NAME=VALUE]NAMEの部分にはカスタムパラメータを設定できます。ここではconfigを与えているのですが、Extension.swift.gyb内のconfigVALUEが代入されます。

実際に生成されたoutput.swiftはこんな感じ。

output.swift
import L

extension Street {
    public var name_lens: Lens<Street, String> {
        return Lens<Street, String>(
            get: { $0.name },
            set: { new_name, obj in 
                var newObj = obj
                newObj.name = new_name
                return obj
            }
        )
    }
}

実際にはコメントも出力されるのですが、ここでは省略してます。

17
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
10