エントリー
少し前まで、PowerShellスクリプトからさまざまなHTMLレポートをレンダリングして、さらに電子メールで送信する作業を行いました。 既製のソリューションの検索では、多くは得られませんでした。 誰かが独自の複雑な
要件の控えめなリストは次のとおりです。
- ビューコードは別のファイルにある必要があります。
- ビュー内では、PowerShellでのネストとコード挿入のサポートが必要です。
- 追加設定なしでPowerShell 2.0を使用するホストで動作するはずです。
そのようなものは何も見つからなかったため、単純な(同時に強力な)レンダリングエンジンが従来のAspのスタイルで実装されました。
実装の詳細
(PowerShell自体と同様に)問題を調べてみると、文字列内のPowerShell式を計算する構文に気付きました。 たとえば、式
" $($env:COMPUTERNAME)"
は実行時に解釈され、出力では
MYCOMPUTER
ようなものが得られます。
実際、これは最も単純な形式でテンプレート化されています。 これにより、非常に複雑なビューをレンダリングできます。
$Model = @{} $Model.Title = 'Hello, this is a test' $Model.Clients = @('Ivan', 'Sergiy', 'John') $html = "<h1> $($Model.Title) </h1> <div class=""test""> <ul> $( foreach($client in $Model.Clients) {" <li> $( $client ) </li> "}) </ul> </div>" $html
ご覧のとおり、PowerShellパーサーを使用すると、文字列に$()で囲まれたコードのネストされた挿入を使用できます。これは、分岐とループの実装に非常に便利です。
この方法は、小さなタスクには既に使用できますが、欠点もあります。
- ビューコードは、個別のファイルではなく、スクリプトコードに含まれています。
- ネストされたビューを使用する方法はありません。
- 構文は少しあいまいで、多くの場合、括弧や引用符が欠落しているため、すべてを厳しくチェックする必要があります。
- テキスト挿入では二重引用符
"
as""
をエンコードする必要があります。
最初の2つの欠点は非常に簡単に解決されます。テンプレートはViewsサブフォルダーの別のファイルに移動され、モデルをレンダリングするための関数が記述されます。
function RenderViewNativePowerShell( [Parameter(Mandatory=$true)][string] $viewName, [Parameter(Mandatory=$true)][Object] $model ) { $viewFileName = Resolve-Path ('Views\' + $viewName) $templateContent = Get-Content $viewFileName | Out-String return $ExecutionContext.InvokeCommand.ExpandString('"' + $templateContent + '"') }
その後、次のように呼び出すことができます。
RenderViewNativePowerShell 'Test_ps.html' $Model
ネストされたビューがサポートされています。 test_ps.htmlは次のようになります。
$( RenderViewNativePowerShell 'header_ps.html' $Model ) <div class=""test""> <ul> $( foreach($client in $Model.Clients) {" <li> $( $client ) </li> "}) </ul> </div>
これで十分なように思えるかもしれませんが、残りの欠点を克服することにしました-ASPブラケット<%...%>の使用に切り替えます。この構文は多くのテキストエディターでサポートされており、ページレイアウトがはるかに読みやすいためです。
したがって、実装の主なアイデアは非常に単純です。すべての角かっこ<%...%>を取得し、PowerShellの同等の$(...)に置き換えます。 いくつかの困難は、ネストされたビューが「...」ブロック内にある必要があるため、ネストされたビューを考慮するために置換があいまいでなければならないことでした。
いくつかの苦痛の後、そのような機能が生じました:
function RenderView( [Parameter(Mandatory=$true)][string] $viewName, [Parameter(Mandatory=$true)][Object] $model ) { $viewFileName = Resolve-Path ("Views\" + $viewName) $templateContent = Get-Content $viewFileName | Out-String $rx = New-Object System.Text.RegularExpressions.Regex('(<%.*?%>)', [System.Text.RegularExpressions.RegexOptions]::Singleline) $res = @() $splitted = $rx.split($templateContent); foreach($part in $splitted) { if ($part.StartsWith('<%') -and $part.EndsWith('%>')) #transform <%...%> blocks { $expr = $part.Substring(2, $part.Length-4) #remove <%%> quotes $normExpr = $expr.Replace('`n','').Replace('`r','').Trim(); $startClosure = '$(' $endClosure = ')' if ($normExpr.endswith('{')) { $endClosure = '"' } if ($normExpr.startsWith('}')) { $startClosure = '"' } $res += @($startClosure + $expr + $endClosure) } else #encode text blocks { $expr = $part.Replace('"', '""'); $res += @($expr) } } $viewExpr = $res -join '' return $ExecutionContext.InvokeCommand.ExpandString('"' + $viewExpr + '"') }
PowerShellでの<%%>の必須置換に加えて、テキストブロック内の「by」の置換も実行されます。
その結果、Visual Studioでのビューは非常によく見えます。
結論として、いくつかのテストとサンプルを含むソースコードがGitHubで利用できることに注意してください。