Posted at

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

More than 3 years have passed since last update.

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
}
)
}
}


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