PowerShellで作るdeliciousクライアント その2
前回の続きです。
ブックマーク一覧の取得 - dirコマンドへの対応
まずはブックマーク一覧の取得機能を実装します。操作的には「delicious」ドライブに移動して、dirコマンド(Get-ChildItemコマンドレット)を実行するとブックマークの一覧が表示されるといった具合です。
テストしやすくする為にdeliciousにアクセスする機能を別クラスとして定義する事にします。
「Delicious.vb」というファイル名でクラスファイルを追加します。
Delicious.vb
Public NotInheritable Class Delicious Private _credential As Net.ICredentials Sub New(ByVal credential As Net.ICredentials) _credential = credential End Sub End Class
deliciousにアクセスする為には認証が必要になるので、コンストラクタでICredentialsのインスタンスを受け取るようにします。
これは後述するPowerShellで使用される認証クラスであるPSCredentialがGetNetworkCredentialメソッドでNetworkCredentialクラスのインスタンスを返すのでこのようにしています。
次に、deliciousブックマークの情報を格納する為のクラスを「DeliciousBookmark.vb」というファイル名のクラスファイルとして追加します。
DeliciousBookmark.vb
<Serializable()> _ <DebuggerStepThrough()> _ Public Class DeliciousBookmark Private _title As String Public Property Title() As String Get Return _title End Get Set(ByVal value As String) _title = value End Set End Property Private _url As String Public Property Url() As String Get Return _url End Get Set(ByVal value As String) _url = value End Set End Property Private _tags As String() Public Property Tags() As String() Get Return _tags End Get Set(ByVal value As String()) _tags = value End Set End Property Private _comment As String Public Property Comment() As String Get Return _comment End Get Set(ByVal value As String) _comment = value End Set End Property Private _date As DateTime Public Property [Date]() As DateTime Get Return _date End Get Set(ByVal value As DateTime) _date = value End Set End Property Public Overrides Function ToString() As String Return String.Format("Title = {0}, Url = {1}, Tags = {2}", Title, Url, String.Join(",", Tags)) End Function End Class
では、ブックマークの一覧を取得する機能を実装します。
GetBookmarksというメソッドで、引数として、取得する開始位置と件数を受け取ります。 返り値としてList(Of DeliciousBookmark)型を返します。
Delicious.vb
Function GetBookmarks(ByVal start As Integer?, ByVal count As Integer?) As List(Of DeliciousBookmark) Dim address = "https://api.del.icio.us/v1/posts/all" Dim queries = New Dictionary(Of String, Object)() If start.HasValue Then queries.Add("start", start.Value) If count.HasValue Then queries.Add("results", count.Value) Dim wc = New Net.WebClient() wc.Credentials = _credential wc.Encoding = Text.Encoding.UTF8 Dim contents = wc.DownloadString(JoinQueries(address, queries)) Dim xml = XDocument.Parse(contents) Dim results = From ele In xml.Descendants("post") _ Select New DeliciousBookmark With { _ .Title = ele.Attribute("description"), _ .Url = ele.Attribute("href"), _ .Tags = CType(ele.Attribute("tag"), String).Split(" "), _ .Comment = ele.Attribute("extended"), _ .Date = ele.Attribute("time") _ } Return New List(Of DeliciousBookmark)(results) End Function Private Shared Function JoinQueries(ByVal address As String, ByVal queries As IDictionary(Of String, Object)) If queries.Count = 0 Then Return address Return address + "?" + String.Join( _ "&", queries.Select(Function(q) String.Format("{0}={1}", q.Key, q.Value)).ToArray() _ ) End Function
処理的には、与えられた引数でURLを組み立て、WebClientクラスを使ってリクエストを送っています。
レスポンスはXMLとして返ってくるので、それをLINQ to XMLを使ってパースを行い、DeliciousBookmark型に変換しています。
使い方としては以下のようになります。
Program.vb
Sub Main() Dim obj = New Delicious(New Net.NetworkCredentials("ユーザ名", "パスワード")) ' 10件だけ取ってくる Dim bookmarks = obj.GetBookmarks(0, 10) ' do something End Sub
次は、このメソッドをプロバイダー側から呼び出すようにします。
そのためにはまず、プロバイダーに認証情報が渡されたかどうかをチェックする必要があります。
認証情報はNewDriveメソッド(New-PSDriveコマンドレットに対応)の引数として渡されるPSDriveInfoクラスのCredentialプロパティからアクセスできます。
DeliciousProvider.vb
Protected Overrides Function NewDrive(ByVal drive As PSDriveInfo) As PSDriveInfo If drive.Credential Is PSCredential.Empty Then Throw New ArgumentException("認証情報[-Credential]を指定して下さい。") Return MyBase.NewDrive(drive) End Function
PSCredentialクラスのEmptyプロパティと比較する事で、認証情報が指定されているかどうかを調べられます。
認証情報を指定してドライブを作る時は以下のようにします。
PS > New-PSDrive delicious Delicious c:\temp -Credential ユーザ名
というコマンドを実行すると、以下のパスワードを入力するダイアログが表示されます。
ここでパスワードを入力して、「OK」ボタンをクリックすればプロバイダーに認証情報を渡す事ができます。
後は、GetChildItem(Get-ChildItemコマンドレットに対応)メソッドをオーバーライドして、その中でGetBookmarksメソッドを呼び出し、WriteItemObjectメソッドでオブジェクトをPowerShellのパイプラインに出力します。
DeliciousProvider.vb
Private _delicious As Delicious Public ReadOnly Property Delicious() As Delicious Get If _delicious Is Nothing Then _delicious = New Delicious(Me.PSDriveInfo.Credential.GetNetworkCredential()) End If Return _delicious End Get End Property Protected Overrides Sub GetChildItems(ByVal path As String, ByVal recurse As Boolean) For Each bookmark In Me.Delicious.GetBookmarks(0, 20) WriteItemObject(bookmark, path, False) Next End Sub
ちなみにDeliciousクラスのインスタンスをプロパティ経由でインスタンス化しているのは、プロバイダークラスはコマンドレットが呼び出される度にインスタンス化が行われる為、クラスのフィールドに格納してもそれが保持され続けるわけでは無いからです。
ここまでで一度ビルドして、PowerShellを起動して「delicious」ドライブにアクセスしてみましょう。
dirコマンドを実行すると以下のように表示されるはずです。
PS delicious:\> dir PSPath : Delicious\Delicious::c:\temp\ PSParentPath : PSChildName : temp PSDrive : delicious PSProvider : Delicious\Delicious PSIsContainer : False Title : Microsoft Semblio - Home Page Url : http://www.microsoft.com/learningspace/semblio/Default.aspx Tags : {Microsoft, Silverlight} Comment : 学習教材を作るサイト Date : 2009/03/11 6:10:36 ....
見づらいですね。Format-Tableなどの整形系のコマンドレットを使えば見易くなりますが、毎回指定するのは面倒なのでオブジェクトの書式設定ファイルを用意します。
psdelicious.format.ps1xml
<?xml version="1.0" encoding="utf-8" ?> <Configuration> <ViewDefinitions> <View> <Name>Tags</Name> <ViewSelectedBy> <TypeName>PSDelicious.DeliciousBookmark</TypeName> </ViewSelectedBy> <ListControl> <ListEntries> <ListEntry> <ListItems> <ListItem> <Label>タイトル</Label> <PropertyName>Title</PropertyName> </ListItem> <ListItem> <Label>URL</Label> <PropertyName>Url</PropertyName> </ListItem> <ListItem> <Label>コメント</Label> <PropertyName>Comment</PropertyName> </ListItem> <ListItem> <Label>タグ</Label> <PropertyName>Tags</PropertyName> </ListItem> <ListItem> <Label>更新日付</Label> <PropertyName>Date</PropertyName> </ListItem> </ListItems> </ListEntry> </ListEntries> </ListControl> </View> </ViewDefinitions> </Configuration>
この書式設定ファイルを以下のようにして、読み込みます。
PS > Update-FormatData -prependPath psdelicious.format.ps1xml
この状態で、再度dirコマンドを実行すると、
PS delicious:\> dir タイトル : Microsoft Semblio - Home Page URL : http://www.microsoft.com/learningspace/semblio/Default.aspx コメント : 学習教材を作るサイト タグ : {Microsoft, Silverlight} 更新日付 : 2009/03/11 6:10:36 ...
とりあえず、これで20件だけですがブックマークの一覧を表示する事ができました。
せっかくなので、オプションを指定して取得する開始位置と件数を指定できるようにしておきましょう。
DeliciousProviderクラスのインナークラスとして、以下のクラスを定義します。
DeliciousProvider.vb
Private Class GetChildItemsParameters Private _start As Integer <Parameter()> _ Public Property Start() As Integer Get Return _start End Get Set(ByVal value As Integer) _start = value End Set End Property Private _count As Integer = 20 <Parameter()> _ Public Property Count() As Integer Get Return _count End Get Set(ByVal value As Integer) _count = value End Set End Property End Class
StartとCountというプロパティを持つだけのクラスです。プロパティはそれぞれParameterAttribute属性でマークしておきます。
GetChildItemsDynamicParametersメソッドをオーバーライドして、このクラスのインスタンスを返しておきます。
DeliciousProvider.vb
Protected Overrides Function GetChildItemsDynamicParameters(ByVal path As String, ByVal recurse As Boolean) As Object Return New GetChildItemsParameters() End Function
DeliciousProvider.vb
Protected Overrides Sub GetChildItems(ByVal path As String, ByVal recurse As Boolean) Dim dynaParams = CType(Me.DynamicParameters, GetChildItemsParameters) For Each bookmark In Me.Delicious.GetBookmarks(dynaParams.Start, dynaParams.Count) WriteItemObject(bookmark, path, False) Next End Sub
これで、以下のように開始位置と取得件数を指定して、dirコマンドを実行することができるようになります。
PS delicious:\> dir -Start 10 -Count 100
次回に続く・・・