LoginSignup
0
0

More than 5 years have passed since last update.

goa (v1): `goagen --regen` と `goagen --force` の挙動 #goadesign

Last updated at Posted at 2018-05-23

goaを使ったサービス開発では、まずAPIやresourceの定義をdesignと呼ばれるコードに記述し → それをinputとしてgeneratorで実稼働コードの雛形を生成 するという開発手順を踏む事になるが、新規でdesignを書くだけではなく既存designの修正時にも同様に再生成作業が発生する事がある。goaが提供するコードジェネレータ goagen には --regen という再生成の為に用意されたと思われるオプションが提供されているが、どういう挙動をするか調査したのでメモ

前提条件

  • goa ver 1.3.0
  • 対象controller actionはGET(参照系処理)
  • controllerのコードに既に非・自動生成なコード(主にビジネスロジック)も混ざっているケースで、controller のdesignに変更が発生 => その変更差分をgoagenを再実行してcontrollerコードに摘要したい。というケース

goaで提供されている goagen --regengoagen --force

goagen 再生成を意識して用意されたと思われるオプションは --regen--force の2つある。例えば goagen main ... コマンドでcontrollerを生成する際にこの2つのオプションがどういう挙動をするかは goagen/gen_main/generator.goの GenerateControllerメソッド のコードを読むと分かる

抜粋
if regen {
  actionImpls, extractedImports, err = extractControllerBody(filename)
  if err != nil {
    return "", err
  }
  os.Remove(filename)
}
if force {
  os.Remove(filename)
}
  • --force の場合はファイルそのものが自動生成ファイルで上書きされる。なので独自処理コードを追加していた場合はそのコードはなくなる
  • --regen の場合は、goagen直後のコードに含まれる start_implement というコメントと end_implement というコメントの存在を探し、それらの間に独自追加されたコードを維持しつつ、それ以外の部分が上書き される
    • つまり独自処理は、この両コメントを残しつつ、両コメントの間に書くと良さそう  

--regen の際、独自追加コード or NOT を判定する為の解析処理が https://github.com/goadesign/goa/blob/master/goagen/gen_main/generator.go#L74で行われている。

  • 現存しているcontrollerコードの // XXXController_ACTIONNAME: start_implement というコメントと、 // XXXController_ACTIONNAME: end_implement というコメントで挟まれているコードブロックを探す
  • このブロック(の各行)を actionImpls[match[1]] = strings.Join(block, "\n") という変数に退避。最終的にこの actionImpls をreturnする
  • このブロック以外(start ... end の外)は、genされたコードで上書きする

という挙動。

この際に end_implement コメントの直後に res := &app.XXXList{} 的な空struct代入コードも復活するのだけど、例えば end_implement コメントより前の独自処理記載部分に res := 処理結果 という代入を行うコードがある場合だと、この空struct代入コードにより結果が res 変数が上書きされる事になる。

こうなる
  // HogeController_SearchHoge: start_implement

  // Put your logic here
  res := 実際の処理結果  // 再生成でも維持

  // HogeController_SearchHoge: end_implement
  res := &app.HogeList{}  // ★再生成で空struct上書きコード復活
  return ctx.OK(res)

これを回避する為、最初「実際の処理結果は res という変数以外の変数を使うようにする」という策を思い付いたのだが、controllerの自動生成では例えばGETの正常系なら return ctx.OK(res) という結果返却コードを生成してくれるので、このreturnで使っている変数も res 以外にrenameする という修正が発生する事になる。つまり自動生成部分に手を加える という事になり、保守性の観点で懸念があるものの、 "--regen way" に乗るなら再生成はこの流れで行う事になりそう。

「空struct代入コードは end_implement より前の行に生成されるようなpull reqを出す」という案も考えたのだけど、空struct代入を end_implement の後ろにしている意図を完全に理解し切れていなかった為、一旦却下

補足: gormaでmodelを管理している場合

gormaはgoaのプラグインとして公開されているgormベースのO/Rマッパーで、このプラグインを導入する事で「modelを独自DSLでdesignとして定義」する事ができるようになり、このdesignをinputとして goagen gen コマンドを実行する事で、gormベースのmodelのコードが自動生成される。デフォルトでは、

  • モデル名.go というコードを生成: 基本的なCRUD処理はココに自動定義される
  • モデル名_helper.go というコードを生成: "controllerで使用可能なレスポンス用型(media_type)への変換を含んだ参照系" 処理はココに自動定義される

という挙動になっており、独自コードは単にファイルを分けて定義するようにすれば、再生成・上書き の考慮は不要になる。例えばgoa公式のサンプルでは独自処理は モデル名_addon.go というファイルに分けている。ただこの場合も、例えば「modelの項目構成が変わった」り、「media_typeの内部定義が変わった」りした場合に、その変更を考慮した修正が独自処理にも発生する事はある。

0
0
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
0
0