PSKoans.psm1
function Get-Enlightenment { <# .NOTES Name: Get-Enlightenment Author: vexx32 .SYNOPSIS Reflect on your progress and check your answers. .DESCRIPTION Get-Enlightenment executes Pester against the koans to evaluate if you have made the necessary corrections for success. .PARAMETER Meditate Opens your local koan folder. .PARAMETER Reset Resets everything in your local koan folder to a blank slate. Use with caution. .EXAMPLE PS> Get-Enlightenment Assesses the results of the Pester tests, and builds the meditation prompt. .EXAMPLE PS> rake Assesses the results of the Pester tests, and builds the meditation prompt. .EXAMPLE PS> rake -Meditate Opens the user's koans folder, housed in $home\PSKoans. If VS Code is in $env:Path, opens in VS Code. .EXAMPLE PS> rake -Reset Prompts for confirmation, before wiping out the user's koans folder and restoring it back to its initial state. .LINK https://github.com/vexx32/PSKoans #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = "Default")] [Alias('Rake', 'Invoke-PSKoans', 'Test-Koans')] param( [Parameter(Mandatory, ParameterSetName = "Meditate")] [switch] $Meditate, [Parameter(Mandatory, ParameterSetName = "Reset")] [switch] $Reset ) switch ($PSCmdlet.ParameterSetName) { "Reset" { Initialize-KoanDirectory } "Meditate" { if (Get-Command -Name 'Code' -ErrorAction SilentlyContinue) { Start-Process -FilePath 'code' -ArgumentList $env:PSKoans:KoanFolder -NoNewWindow } else { Invoke-Item $env:PSKoans:KoanFolder } } "Default" { Clear-Host Write-MeditationPrompt -Greeting $SortedKoanList = Get-ChildItem "$env:PSKoans:KoanFolder" -Recurse -Filter '*.Koans.ps1' | Get-Command {$_.FullName} | Where-Object {$_.ScriptBlock.Attributes.TypeID -match 'KoanAttribute'} | Sort-Object { $_.ScriptBlock.Attributes.Where( { $_.TypeID -match 'KoanAttribute' }).Position } | Select-Object -ExpandProperty Path $PesterTestCount = Invoke-Pester -Script $SortedKoanList -PassThru -Show None | Select-Object -ExpandProperty TotalCount $KoansPassed = 0 foreach ($KoanFile in $SortedKoanList) { $PesterTests = Invoke-Pester -Script $KoanFile -PassThru -Show None $KoansPassed += $PesterTests.PassedCount if ($PesterTests.FailedCount -gt 0) { break } } if ($PesterTests.FailedCount -gt 0) { $NextKoanFailed = $PesterTests.TestResult | Where-Object Result -eq 'Failed' | Select-Object -First 1 $Meditation = @{ DescribeName = $NextKoanFailed.Describe Expectation = $NextKoanFailed.ErrorRecord ItName = $NextKoanFailed.Name Meditation = $NextKoanFailed.StackTrace KoansPassed = $KoansPassed TotalKoans = $PesterTestCount } Write-MeditationPrompt @Meditation } else { $Meditation = @{ Complete = $true KoansPassed = $KoansPassed TotalKoans = $PesterTestCount } Write-MeditationPrompt @Meditation } } } } # end function function Get-Blank { [Alias('__', 'FILL_ME_IN')] param() $null } function Write-MeditationPrompt { <# .NOTES Name: Write-MeditationPrompt Author: vexx32 .SYNOPSIS Provides simplified and targeted output for koan test results. Only shows the next failing koan; all other output is suppressed. .DESCRIPTION Provides a mechanism for Get-Enlightenment to write clean output. #> [CmdletBinding(DefaultParameterSetName = 'Meditation')] param( [Parameter(Mandatory, ParameterSetName = "Meditation")] [ValidateNotNullOrEmpty()] [string] $DescribeName, [Parameter(Mandatory, ParameterSetName = "Meditation")] [ValidateNotNullOrEmpty()] [string] $Expectation, [Parameter(Mandatory, ParameterSetName = "Meditation")] [ValidateNotNullOrEmpty()] [string] $ItName, [Parameter(Mandatory, ParameterSetName = "Meditation")] [ValidateNotNullOrEmpty()] [string] $Meditation, [Parameter(Mandatory, ParameterSetName = "Meditation")] [Parameter(Mandatory, ParameterSetName = 'Enlightened')] [ValidateNotNull()] [int] $KoansPassed, [Parameter(Mandatory, ParameterSetName = "Meditation")] [Parameter(Mandatory, ParameterSetName = 'Enlightened')] [ValidateNotNull()] [int] $TotalKoans, [Parameter(Mandatory, ParameterSetName = 'Greeting')] [switch] $Greeting, [Parameter(Mandatory, ParameterSetName = 'Enlightened')] [switch] $Complete ) $Red = @{ForegroundColor = "Red"} $Blue = @{ForegroundColor = "Cyan"} $Koan = $env:PSKoans:Meditations | Get-Random $SleepTime = @{Milliseconds = 50} #region Prompt Text $Prompts = @{ Welcome = @" Welcome, seeker of enlightenment. Please wait a moment while we examine your karma... "@ Describe = @" Describing '$DescribeName' has damaged your karma. "@ TestFailed = @" You have not yet reached enlightenment. The answers you seek... "@ Expectation = $Expectation Meditate = @" Please meditate on the following code: "@ Subject = @" [It] $ItName $Meditation "@ Wisdom = @" $($Koan -replace "`n","`n ") Your path thus far: "@ OpenFolder = @" You may run 'rake -Meditate' to begin your meditation. "@ Completed = @" Congratulations! You have taken the first steps towards enlightenment. You cast your gaze back upon the path that you have walked: "@ BookSuggestion = @" If you would like to further your studies in this manner, consider investing in 'PowerShell by Mistake' by Don Jones - https://leanpub.com/powershell-by-mistake "@ } #endregion Prompt Text switch ($PSCmdlet.ParameterSetName) { 'Greeting' { Write-Host -ForegroundColor Cyan $Prompts['Welcome'] break } 'Meditation' { Write-Host @Red $Prompts['Describe'] Start-Sleep @SleepTime Write-Host @Blue $Prompts['TestFailed'] Write-Host @Red $Prompts['Expectation'] Start-Sleep @SleepTime Write-Host @Blue $Prompts['Meditate'] Write-Host @Red $Prompts['Subject'] Start-Sleep @SleepTime Write-Host @Blue $Prompts['Wisdom'] $ProgressAmount = "$KoansPassed/$TotalKoans" [int]$ProgressWidth = $host.UI.RawUI.WindowSize.Width * 0.8 - ($ProgressAmount.Length + 4) $PortionDone = ($KoansPassed / $TotalKoans) * $ProgressWidth " [{0}{1}] {2}" -f @( "$([char]0x25a0)" * $PortionDone "$([char]0x2015)" * ($ProgressWidth - $PortionDone) $ProgressAmount ) | Write-Host @Blue Write-Host @Blue $Prompts['OpenFolder'] break } 'Enlightened' { Write-Host @Blue $Prompts['Completed'] $ProgressAmount = "$KoansPassed/$TotalKoans" [int]$ProgressWidth = $host.UI.RawUI.WindowSize.Width * 0.8 - ($ProgressAmount.Length + 4) $PortionDone = ($KoansPassed / $TotalKoans) * $ProgressWidth " [{0}{1}] {2}" -f @( "$([char]0x25a0)" * $PortionDone "$([char]0x2015)" * ($ProgressWidth - $PortionDone) $ProgressAmount ) | Write-Host @Blue Write-Host @Blue $Prompts['BookSuggestion'] break } } } function Initialize-KoanDirectory { <# .NOTES Name: Initialize-KoanDirectory Author: vexx32 .SYNOPSIS Provides a blank slate for Koans. .DESCRIPTION If Koans folder already exists, the folder(s) are overwritten. Otherwise a new folder structure is produced. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")] param() if ($PSCmdlet.ShouldProcess($env:PSKoans:KoanFolder, "Restore the koans to a blank slate")) { if (Test-Path -Path $env:PSKoans:KoanFolder) { Write-Verbose "Removing the entire koans folder..." Remove-Item -Recurse -Path $env:PSKoans:KoanFolder -Force } Write-Debug "Copying koans to folder" Copy-Item -Path "$PSScriptRoot/Koans" -Recurse -Destination $env:PSKoans:KoanFolder Write-Verbose "Koans copied to '$env:PSKoans:KoanFolder'" } else { Write-Verbose "Operation cancelled; no modifications made to koans folder." } } ${env:PSKoans:Meditations} = Import-CliXml -Path ("$PSScriptRoot/Data/Meditations.clixml") ${env:PSKoans:KoanFolder} = $Home | Join-Path -ChildPath 'PSKoans' if (-not (Test-Path -Path $env:PSKoans:KoanFolder)) { Initialize-KoanDirectory -Confirm:$false } |