web-dev-qa-db-ja.com

ifステートメントでラップしないとエラーが発生する可能性がある式に基づいて、Goテンプレートに変数を条件付きで設定するにはどうすればよいですか?

質問

次のようにするにはどうすればよいですか。

{{ $use_ssl := (ne $.Env.CERT_NAME "") }}

ここで、$.Env.CERT_NAMEはnil /未定義の場合があります。 nilの場合、次のエラーが発生します。

at <ne $.Env.CERT_NAME "">: error calling ne: invalid type for comparison

注:Goテンプレートに渡されるオブジェクトを制御することはできないため、テンプレート自体の中でこれを完全に解決する必要があります。

私が試したこと

私はそれが空でないかどうかを最初にチェックすることで回避しようとしました:

{{ $use_ssl := ( ($.Env.CERT_NAME) && (ne $.Env.CERT_NAME "") ) }}

しかし、それはこのエラーを出します:

unexpected "&" in operand

だから私はこれに切り替えました、これは構文的に許可されています:

{{ $use_ssl := (and ($.Env.CERT_NAME) (ne $.Env.CERT_NAME "") ) }}

しかし、私は&&演算子で取得する 短絡評価 を失い、再びこのエラーを取得します。

at <ne $.Env.CERT_NAME ...>: error calling ne: invalid type for comparison

わかりました、いいワンライナーでそれを実行できない場合、そして Goに三項演算子がない 、それは問題ありません、私はそれをやります 慣用的なGo way 、明らかにif/elseです。

{{ if $.Env.CERT_NAME }}
  {{ $use_ssl := (ne $.Env.CERT_NAME "") }}
{{ else }}
  {{ $use_ssl := false }}
{{ end }}

しかしもちろん、スコーピングの問題が発生します。ifが不可解に(または少なくとも厄介なことに)新しい変数スコープを作成するため(慣れているRuby/ERBテンプレートとは異なります)、明らかにできません。これも行う:

{{ if $.Env.CERT_NAME }}
  {{ $use_ssl := true }}
{{ else }}
  {{ $use_ssl := false }}
{{ end }}
# $use_ssl: {{ $use_ssl }}

このエラーが発生することなく:

undefined variable "$use_ssl"

「汗はない」と思った。外部スコープで変数を宣言するだけなので、正しいスコープを持ち、内部スコープはその変数を変更できます( 1つは外側のスコープ)。 (ちなみに、これがRubyでの動作方法です。)

いいえ、どうやっても、同じ名前でスコープが異なる2つの異なる変数を作成するだけです(混乱を招く方法は次のとおりです)。

{{ $use_ssl := false }}
{{ if $.Env.CERT_NAME }}
  {{ $use_ssl := true }}
  # $use_ssl: {{ $use_ssl }}
{{ else }}
  {{ $use_ssl := false }}
{{ end }}
# $use_ssl: {{ $use_ssl }}

  # $use_ssl: true
# $use_ssl: false

また、次のようなifステートメントをインライン化してみました。

{{ $use_ssl := if $.Env.CERT_NAME { true } }}

しかし、それはこのエラーを与えます:

unexpected <if> in command

どうやらifステートメントは、RubyのようにGoの式として使用できないのですか?

で提案されている三項演算子のさまざまな代替手段を試した場合も、構文エラーが発生します。Cの三項演算子に相当する慣用的なGoとは何ですか

c := map[bool]int{true: 1, false: 0} [5 > 4]

そして確かに、あなたは$.somethingで内部スコープの外部スコープから変数を読み取ることができますが、どのようにしてください set$.somethingは内部スコープにありますか?

そうでなければ、どうやって??

それで私は何を逃したのですか?これはGoテンプレートでも可能ですか?

Goテンプレート(私はそれらに慣れていない)は非常に制限されているようです。 Go自体でできるほとんどすべてのことは、テンプレートでは不可能です。しかし、うまくいけば回避策があります...

http://play.golang.org/p/SufZdsx-1v には、{{$hasFemale := cell false}}で新しいオブジェクトを作成し、$hasFemale.Set trueですが、テンプレートを評価するコード(template.FuncMapを呼び出しているコード)にアクセスせずに、どうすればこのようなことができますか?

試行錯誤したその他の人

https://groups.google.com/forum/#!topic/golang-nuts/MUzNZ9dbHrg

これがGoで私が抱えている最大の(そして唯一の)問題です。この機能がまだ実装されていない理由がわかりません。

テンプレートパッケージが一般的なコンテキストで使用される場合(たとえば、任意のテンプレートファイルを任意のjsonファイルで実行するなど)、事前計算を行う余裕がない場合があります。

https://github.com/golang/go/issues/10608

これはおそらく設計されたとおりですが、変更された$ vを条件外で取得する方法があった場合、それは本当にすばらしいでしょう。

これは、Hugo( https://github.com/spf13/hugo )で私たちが乗り越えるテンプレートの質問の1つ目です。 hack [$.Scratch.Set "v1" 123]を追加して、今のところそれを回避します...

19
Tyler Rick

現在機能しているソリューション

setを使用するかmergeを使用して、変数を宣言するのではなくスコープを更新すると、if句の外からアクセスできるようになります。

{{- if eq .podKind "core" -}}
{{- $dummy := set . "matchNodePurpose" .Values.scheduling.corePods.nodeAffinity.matchNodePurpose }}
{{- else if eq .podKind "user" -}}
{{- $dummy := set . "matchNodePurpose" .Values.scheduling.userPods.nodeAffinity.matchNodePurpose }}
{{- end -}}

# Will now output what you decided within an if/else if clause
{{- .matchNodePurpose }}

今後のソリューション(Helm 2.10で利用可能)

Sprig などのいくつかの追加機能がプリインストールされたgoテンプレートであるHelmテンプレートのコンテキストで同じ質問に遭遇しました。

2018年3月28日、このprで TerenaryオペレーターがSprigに追加され、Helmはそのうちにあなたと私が抱えている問題を解決するようになります。

terenary

true | ternary "b" "c" 戻ります "b"

false | ternary "b" "c" 戻ります "c"

9
consideRatio

特定の問題の回避策を見つけたと思いますが、問題をより一般的に解決する他の回答を聞きたいです。

https://github.com/jwilder/nginx-proxy/blob/1e0b930/nginx.tmpl#L91

{{ $default_Host := or ($.Env.DEFAULT_Host) "" }}

試してみる別のアイデアが得られました。解決策はorの組み合わせを使用し、追加の一時変数を設定することだと思います...

{{ $cert_name := or $.Env.CERT_NAME "" }}
{{ $use_ssl := ne $cert_name "" }}
5
Tyler Rick