Toolmaking Month of Lunches: Lab 8A

by davidski at 2012-12-30 08:14:11

Apologies in advance if there’s a better forum for this than the general Q&A. I’m open to redirection!

Working through the Toolmaking Month of Lunches book, I’m a bit puzzled by Chapter 8 Lab A. One of the requirements of that section is to insert code that will ensure that the mandatory string array parameter ComputerName will never take a null or empty parameter. The solution to this in the online PDF is the use of [ValidateNotNullorEmpty] option. While that makes sense, it’s my understanding (confirmed via get-help about_functions_advanced_parameters) that [string]$computerName, as a string parameter, will never accept nulls or empty valuations. When I’ve tried out code both with and without the validate option, both correctly error out, with the only difference being a slight change in error text.

Sure, for other data types the [validateNotNullorEmpty] option may be helpful, but I’m not sure why that’s the solution for this particular case. Am I missing hidden utility for this validate option here?

David
by DonJ at 2012-12-30 12:37:19
Well… I’m not sure if you’re missing something or not.


function test {
[CmdletBinding()]
param(
[string]$those
)
$count = 1;
foreach ($that in $those) {
write "$count $that"
$count++
}
}

test $null


That passes a null set to $those. No error.


function test {
[CmdletBinding()]
param(
[ValidateNotNullOrEmpty()]
[string]$those
)
$count = 1;
foreach ($that in $those) {
write "$count $that"
$count++
}
}

test $null


Now returns an error. Difference being there’s no Mandatory declaration, right?


function test {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[string]$those
)
$count = 1;
foreach ($that in $those) {
write "$count $that"
$count++
}
}

test $null


Returns an error.


function test {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True)]
[string]$those
)
$count = 1;
foreach ($that in $those) {
write "$count $that"
$count++
}
}

test $null


Returns an error - but it’s a different one. In this case, it accepts $null, but has an issue because it can’t match the data type. In the previous examples, you got a validation error from ValidateNotNullOrEmpty(); in this last one, you got a "cannot bind parameter" error. There’s a subtle difference. I tend to prefer (a) code that self-documents and (b) the most specific error messages possible. The validation attribute provides a more specific error. I guess it’s not so much a matter of "make sure the function won’t run if $null is passed" as it is "make sure it’s clear that $null is not acceptable." In the latter case, technically, $those did accept $null. It just didn’t like it. In the former, $those explicitly didn’t accept $null. My validation threw the error, as opposed to the shell’s parameter binding code throwing the error. If you trace the command, you get an entirely different result with the validation attribute.

In the larger meta-sense, a validation attribute is something an external UI could interpret, providing UI-level feedback to the user. Simply relying on [string] not accepting $null isn’t something you can statically analyze. So again, having the validation attribute kind of serves to document what’s acceptable, as opposed to simply letting the shell barf.
by davidski at 2013-01-01 15:07:11
So this is a stylistic point rather than functionality question. Transitioning from a script view (where I’d hope relying on the behavior of system primitives would be justifiable) to a bigger tool-based approach, making as much in your code explicit as possible makes sense. Thanks for the detailed response.

As an aside, is there any particular reason Toolmaking in a Month of Lunches uses the WMI rather than the V3 CIM cmdlets?

David
by DonJ at 2013-01-01 15:28:35
Yyyyyyeah, stylistic a bit. Definitely more "better practices for toolmaking" which involves giving code to other people. Spot-on there.

As for the WMI thing, yeah. Backward compatibility. We tried to build as much of the book as we could to still work with v2, and vast wodges of it do. WMI works all the way back to NT 4, after all, whereas CIM doesn’t go nearly as far back. Pros and cons. When we rev the thing for the next version, we’ll revisit it again. Right now, with SO many people still stuck on XP, I’m just partial to supporting that as much as possible.