[RedditRSS] -- Staying Classy with PS5 Part 2

Posted
Comments None

Last time we looked at a simple PS5 class for loading and viewing the RSS feeds from subreddits. If you’d like to look at the class definition in its entirety, you can check out that post here.

Let’s break it down piece by piece. First, after declaring the class keyword and the name of the class, we define the members. Notice that for two out of three of the members (properties), we type constrained them.

class RedditRSS {
    [string]$URI
    [datetime]$LastUpdate
    $Feed

The reason we can’t easily type constrain the $feed member is that it’s a specific XML format that PowerShell doesn’t already recognize. If we wanted, we could put [psobject] as the type, but that doesn’t really say much. The only danger here is that a user might try to directly write to the .feed member and break all the methods we already defined.

To prevent that, we can optionally hide a member by placing “hidden” in front of the definition (and type). That would look like this

class RedditRSS {
    [string]$URI
    [datetime]$LastUpdate
    hidden $Feed

I’ve chosen not to do this because it’s likely we will want to directly access the feed so it is better not to have to search for it. Hiding the member doesn’t make it inaccessible, but it will not be returned unless specified. To access a hidden member, you must specify it by name, such as

$myobject.Feed

or

$myobject | Select-Object Feed

Tip: to view hidden members of an object, pipe the object to

$myobject | Get-Member -Force

Constructors

If you recall, the easiest way to create an instance of our custom class is using the static constructor method. Methods are defined by the name of the method, parameters in parentheses, and then the code in curly braces. Our RedditRSS constructor looks like

    RedditRSS ([string]$URI) {
        SWITCH ($URI) {
            {$_.endswith("/")} {
                #If you copy the URL from your browser
                $this.URI = ($URI.Substring(0,($URI.Length -1))) + ".rss"
            }
            {!$_.EndsWith("/") -and !$_.EndsWith(".rss")} {
                #As above but missing the slash
                $this.URI = $URI + ".rss"
            }
            default {
                $this.URI = $URI
            }
        }
        $this.LastUpdate = Get-Date
        $this.Feed = Invoke-RestMethod $this.URI
    }

The only parameter it requires is the URL, which we’ve constrained to the [string] type. It could have been type constrained to [uri] and provided more robust error prevention, but this is meant to be a simple demo :) The switch statement should be self-explanatory. With it we make sure the the URL passed either ends with “.rss” and adds it if not.

Obviously what sticks out compared to creating a custom function is the $this variable. That always refers to the object we’re defining. So, we can set the internal property values and call one method from another, if we want.

Overload Definitions

You’ll see farther down we defined a method called “GetThread” with two parameters, [uint16] Index, and [boolean] (that is, true or false) Refresh. It returns a [PSObject]. There are a few things worth noting about this method.

    [PSCustomObject] GetThread ([uint16]$Index,[boolean]$Refresh) {
        IF ($Refresh) {
            $this.Refresh()
            $Output = Write-Output ($this.Feed[$Index] | Select-Object @{n='Title';e={$_.Title}},
                @{n='Author';e={$_.author.name -replace "^(/u/)"}},
                @{n='Updated';e={Get-Date $_.Updated}},
                @{n='Post';e={$this.ReplaceHTMLNumbers(($_.Content.'#Text' -replace "<.*?>"))}})
            RETURN $Output
        }
        ELSE {
            $Output = Write-Output ($this.Feed[$Index] | Select-Object @{n='Title';e={$_.Title}},
                @{n='Author';e={$_.author.name -replace "^(/u/)"}},
                @{n='Updated';e={Get-Date $_.Updated}},
                @{n='Post';e={$this.ReplaceHTMLNumbers(($_.Content.'#Text' -replace "<.*?>"))}})
            RETURN $Output
        }
    }

First, notice there are several instances of the Return keyword. That is because the class will error out unless the keyword appears at the end of each and every branch of logic. If you use IF THEN and ELSE, you must Return data in each case. This also applies to SWITCH statements like the one we used above. This means we have to be a little more thoughtful about how we define our methods.

Second, that second parameter looks a little cumbersome, doesn’t it? Wouldn’t it be better just to use a [switch] parameter like we do in custom CMDlets? Well, unfortunately we cannot use switches in methods. $myobject.GetThread($uri,switch) is improper syntax. In CMDlets, a switch is a way to set a [boolean] parameter that is false by default. To get around having to always specify every parameter, we can create “overload definitions.”

An overload definition is an alternate definition for a method that accepts a different set of parameters. We have only one overload definition in our class

    [PSCustomObject] GetThread ([uint16]$Index) {
        RETURN $this.GetThread($Index,$false)
    }

Does it make sense? If we want to use the GetThread method, but do not want the data to be refreshed first, we can omit that parameter completely. So

$myobject.GetThread(0)

is functionally identical to

$myobject.GetThread(0,$false)

My hope is that you’ll start to think of how a custom class could make some of your more complicated repeat tasks easier. PowerShell should make our lives easier, and let us figure out the complicated things once, instead of over and over.

Author

Comments

There are currently no comments on this article.

Comment

Enter your comment below. Fields marked * are required. You must preview your comment before submitting it.





← Older Newer →