抽象地談論 Scala 的確有趣,然而一旦將其付諸實踐,就會發現將它作為 “玩具” 與在工作中使用它的區別。Scala 狂熱者 Ted Neward 撰寫了一篇 對 Scitter 的介紹,Scitter 是一個用於訪問 Twitter 的 Scala 庫,本文是其後續篇,在本文中,Ted Neward 為這個客戶機庫提供了一組更有趣也更有用的特性。
歡迎回來,Scala 迷們。上個月,我們談到了 Twitter,這個微博客站點目前正引起社會性網路的極大興趣,我們還談到它的基於 XML-/REST 的 API 如何使它成為開發人員進行研究和探索的一個有趣平台。為此,我們首先充實了 “Scitter” 的基本結構,Scitter 是用於訪問 Twitter 的一個 Scala 庫。
|
我們對於 Scitter 有幾個目標:
在這一期,我們不必完成整個 Twitter API,但是我們將完成一些核心部分,目的是讓這個庫達到公共源代碼控制庫的程度,便於其他人來完成這項工作。
到目前為止:Scitter 0.1
首先我們簡單回顧一下到目前為止我們所處的階段:
package com.tedneward.scitter { import org.apache.commons.httpclient._, auth._, methods._, params._ import scala.xml._ /** * Status message type. This will typically be the most common message type * sent back from Twitter (usually in some kind of collection form). Note * that all optional elements in the Status type are represented by the * Scala Option[T] type, since that's what it's there for. */ abstract class Status { /** * Nested User type. This could be combined with the top-level User type, * if we decide later that it's OK for this to have a boatload of optional * elements, including the most-recently-posted status update (which is a * tad circular). */ abstract class User { val id : Long val name : String val screenName : String val description : String val location : String val profileImageUrl : String val url : String val protectedUpdates : Boolean val followersCount : Int } /** * Object wrapper for transforming (format) into User instances. */ object User { /* def fromAtom(node : Node) : Status = { } */ /* def fromRss(node : Node) : Status = { } */ def fromXml(node : Node) : User = { new User { val id = (node \ "id").text.toLong val name = (node \ "name").text val screenName = (node \ "screen_name").text val description = (node \ "description").text val location = (node \ "location").text val profileImageUrl = (node \ "profile_image_url").text val url = (node \ "url").text val protectedUpdates = (node \ "protected").text.toBoolean val followersCount = (node \ "followers_count").text.toInt } } } val createdAt : String val id : Long val text : String val source : String val truncated : Boolean val inReplyToStatusId : Option[Long] val inReplyToUserId : Option[Long] val favorited : Boolean val user : User } /** * Object wrapper for transforming (format) into Status instances. */ object Status { /* def fromAtom(node : Node) : Status = { } */ /* def fromRss(node : Node) : Status = { } */ def fromXml(node : Node) : Status = { new Status { val createdAt = (node \ "created_at").text val id = (node \ "id").text.toLong val text = (node \ "text").text val source = (node \ "source").text val truncated = (node \ "truncated").text.toBoolean val inReplyToStatusId = if ((node \ "in_reply_to_status_id").text != "") Some((node \"in_reply_to_status_id").text.toLong) else None val inReplyToUserId = if ((node \ "in_reply_to_user_id").text != "") Some((node \"in_reply_to_user_id").text.toLong) else None val favorited = (node \ "favorited").text.toBoolean val user = User.fromXml((node \ "user")(0)) } } } /** * Object for consuming "non-specific" Twitter feeds, such as the public timeline. * Use this to do non-authenticated requests of Twitter feeds. */ object Scitter { /** * Ping the server to see if it's up and running. * * Twitter docs say: * test * Returns the string "ok" in the requested format with a 200 OK HTTP status code. * URL: http://twitter.com/help/test.format * Formats: xml, json * Method(s): GET */ def test : Boolean = { val client = new HttpClient() val method = new GetMethod("http://twitter.com/help/test.xml") method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.executeMethod(method) val statusLine = method.getStatusLine() statusLine.getStatusCode() == 200 } /** * Query the public timeline for the most recent statuses. * * Twitter docs say: * public_timeline * Returns the 20 most recent statuses from non-protected users who have set * a custom user icon. Does not require authentication. Note that the * public timeline is cached for 60 seconds so requesting it more often than * that is a waste of resources. * URL: http://twitter.com/statuses/public_timeline.format * Formats: xml, json, rss, atom * Method(s): GET * API limit: Not applicable * Returns: list of status elements */ def publicTimeline : List[Status] = { import scala.collection.mutable.ListBuffer val client = new HttpClient() val method = new GetMethod("http://twitter.com/statuses/public_timeline.xml") method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.executeMethod(method) val statusLine = method.getStatusLine() if (statusLine.getStatusCode() == 200) { val responseXML = XML.loadString(method.getResponseBodyAsString()) val statusListBuffer = new ListBuffer[Status] for (n <- (responseXML \\ "status").elements) statusListBuffer += (Status.fromXml(n)) statusListBuffer.toList } else { Nil } } } /** * Class for consuming "authenticated user" Twitter APIs. Each instance is * thus "tied" to a particular authenticated user on Twitter, and will * behave accordingly (according to the Twitter API documentation). */ class Scitter(username : String, password : String) { /** * Verify the user credentials against Twitter. * * Twitter docs say: * verify_credentials * Returns an HTTP 200 OK response code and a representation of the * requesting user if authentication was successful; returns a 401 status * code and an error message if not. Use this method to test if supplied * user credentials are valid. * URL: http://twitter.com/account/verify_credentials.format * Formats: xml, json * Method(s): GET */ def verifyCredentials : Boolean = { val client = new HttpClient() val method = new GetMethod("http://twitter.com/help/test.xml") method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)) client.getParams().setAuthenticationPreemptive(true) val creds = new UsernamePasswordCredentials(username, password) client.getState().setCredentials( new AuthScope("twitter.com", 80, AuthScope.ANY_REALM), creds) client.executeMethod(method) val statusLine = method.getStatusLine() statusLine.getStatusCode() == 200 } } } |
[火星人 ] 面向 Java 開發人員的 Scala 指南: 增強 Scitter 庫已經有643次圍觀