抽象地討論 Scala 是一件有趣的事情
Twitter 迅速占領了 Internet 市場
關於本系列
Ted Neward 將深入探討 Scala 編程語言
就其本身而言
由於 Twitter 很早就發布了其 API
考慮到 Scala 的功能性(這看上去能很好地協同 Twitter 的 REST 式特性)以及非常出眾的 XML 處理特性
何為 Twitter?
在詳細討論之前
簡單來說
最具 REST 特征 是什麼意思?
一些讀者會對我所使用的最具 REST 特征 短語感到好奇;這需要一些說明
從實際的角度來說
public_timeline
返回設定了自定義用戶圖標的
非保護用戶的
注意
因此頻繁請求它不再浪費資源
URL
格式
方法
API 限制
返回
從編程的角度來說
清單
<feed xml:lang=
<title>Twitter / tedneward</title>
<id>tag:
<link type=
<updated>
<subtitle>Twitter updates from Ted Neward / tedneward
<entry>
<title>tedneward: @kdellison Happens to the best of us
<content type=
<id>tag:
<published>
<updated>
<link type=
/>
<link type=
_production/profile_images/
<author>
<name>Ted Neward</name>
<uri>;/uri>
</author>
</entry>
</feed>
部的話)都很直觀
由於我們可以采用三種基於 XML 的格式使用 Twitter 消息
清單
<![CDATA[
package com
{
class ScitterTest
{
import org
@Test def simpleAtomParse =
{
val atom =
<feed xml:lang=
<title>Twitter / tedneward</title>
<id>tag:
<link type=
<updated>
<subtitle>Twitter updates from Ted Neward / tedneward
<entry>
<title>tedneward: @kdellison Happens to the best of us
<content type=
Happens to the best of us
<id>tag:
;/id>
<published>
<updated>
<link type=
/>
<link type=
_production/profile_images/
<author>
<name>Ted Neward</name>
<uri>;/uri>
</author>
</entry>
</feed>
assertEquals(atom \\
}
}
}
]]>
有關 Scala 的 XML 支持的更多詳細信息
實際上
API 設計
在深入了解 Scala/Twitter 庫的 API 設計之前(根據同事 ThoughtWorker Neal Ford 的建議
首先
其次
第三
最後
網絡訪問需要一些形式的 HTTP 通信
結果
由於最易於使用的 Twitter API 是測試 API
因此在請求格式中返回
我們將使用它作為 Scitter 測試中的保留條款
清單
package com
{
class ExplorationTests
{
//
import mons
@Test def callTwitterTest =
{
val testURL =
// HttpClient API
val client = new HttpClient()
val method = new GetMethod(testURL)
method
new DefaultHttpMethodRetryHandler(
client
val statusLine = method
assertEquals(statusLine
assertEquals(statusLine
}
}
}
此代碼中最重要的一部分是 HttpClient 樣板 — 感興趣的讀者應該查閱 HttpClient API 文檔了解詳細信息
鑒於此
清單
package com
{
/**
* Object for consuming
* Use this to do non
*/
object Scitter
{
import mons
/**
* Ping the server to see if it
*
* Twitter docs say:
* test
* Returns the string
* URL:
* Formats: xml
* Method(s): GET
*/
def test : Boolean =
{
val client = new HttpClient()
val method = new GetMethod(
method
new DefaultHttpMethodRetryHandler(
client
val statusLine = method
statusLine
}
}
/**
* Class for consuming
* thus
* behave accordingly (according to the Twitter API documentation)
*/
class Scitter(username : String
{
}
}
目前
由於已經明確區分了驗證和未驗證 Twitter 客戶機
這引出了 Twitter 如何驗證用戶的概念
清單
package com
{
class ExplorationTests
{
def testUser =
def testPassword =
@Test def verifyCreds =
{
val client = new HttpClient()
val verifyCredsURL =
val method = new GetMethod(verifyCredsURL)
method
new DefaultHttpMethodRetryHandler(
client
val defaultcreds = new UsernamePasswordCredentials(testUser
client
AuthScope
client
val statusLine = method
assertEquals(
assertEquals(
}
}
}
注意
完成後
清單
package com
{
import mons
//
/**
* Class for consuming
* thus
* behave accordingly (according to the Twitter API documentation)
*/
class Scitter(username : String
{
/**
* Verify the user credentials against Twitter
*
* Twitter docs say:
* verify_credentials
* Returns an HTTP
* requesting user if authentication was successful; returns a
* code and an error message if not
* user credentials are valid
* URL: _credentials
* Formats: xml
* Method(s): GET
*/
def verifyCredentials : Boolean =
{
val client = new HttpClient()
val method = new GetMethod(
method
new DefaultHttpMethodRetryHandler(
client
val creds = new UsernamePasswordCredentials(username
client
new AuthScope(
client
val statusLine = method
statusLine
}
}
}
清單
清單
package com
{
class ScitterTests
{
import org
import com
def testUser =
def testPassword =
//
@Test def verifyCreds =
{
val scitter = new Scitter(testUser
val result = scitter
assertTrue(result)
}
}
}
不算太糟
從 XML 到對象
現在可以添加的最簡單的 API 是 public_timeline
現在
清單
package com
{
class ExplorationTests
{
//
@Test def callTwitterPublicTimeline =
{
val publicFeedURL =
// HttpClient API
val client = new HttpClient()
val method = new GetMethod(publicFeedURL)
method
new DefaultHttpMethodRetryHandler(
client
val statusLine = method
assertEquals(statusLine
assertEquals(statusLine
val responseBody = method
System
System
}
}
}
運行後
清單
<statuses type=
<status>
<created_at>Tue Mar
<id>
<text>She really is
<source><a >twitterrific</a>
</source>
<truncated>false</truncated>
<in_reply_to_status_id></in_reply_to_status_id>
<in_reply_to_user_id></in_reply_to_user_id>
<favorited>false</favorited>
<user>
<id>
<name>Brittanie</name>
<screen_name>brittaniemarie</screen_name>
<description>I
<location>Atlanta or Philly
<profile_image_url>_production/profile_images/
<url>;/url>
<protected>false</protected>
<followers_count>
</user>
</status>
<status>
<created_at>Tue Mar
<id>
<text>Number
<source>web</source>
<truncated>false</truncated>
<in_reply_to_status_id></in_reply_to_status_id>
<in_reply_to_user_id></in_reply_to_user_id>
<favorited>false</favorited>
<user>
<id>
<name>Dale Greenwood</name>
<screen_name>Greeendale</screen_name>
<description>Vegetarian
Love helping people become prosperous</description>
<location>Melbourne Australia</location>
<profile_image_url>_production/profile_images/
<url>;/url>
<protected>false</protected>
<followers_count>
</user>
</status>
(A lot more have been snipped)
</statuses>
通過查看結果和 Twitter 文檔可以看出
清單
package com
{
class ExplorationTests
{
//
@Test def simplePublicFeedPullAndParse =
{
val publicFeedURL =
// HttpClient API
val client = new HttpClient()
val method = new GetMethod(publicFeedURL)
method
new DefaultHttpMethodRetryHandler(
val statusCode = client
val responseBody = new String(method
val responseXML = scala
val statuses = responseXML \\
for (n <
{
n match
{
case { contents @ _*} =>
{
System
contents
c match
{
case { t @ _*} =>
System
case { contents
{
contents
c
{
case { u } =>
System
case _ =>
()
}
)
}
case _ =>
()
}
)
}
case _ =>
() // or
}
}
}
}
}
隨著示例代碼模式的變化
清單
for (n <
{
val text = (n \\
val screenName = (n \\
}
這顯然更加簡短
我們可以強制 Scala 的 XML 庫針對每個元素或子元素遍歷一次圖
我們仍然需要直接處理 XML 消息的結構
也就是說
這又造成了另一個與各格式本身相關的問題
清單
abstract class Status
{
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
}
這與 User 方式相類似
現在
清單
/**
* Object wrapper for transforming (format) into Status instances
*/
object Status
{
def fromXml(node : scala
{
new Status {
val createdAt = (node \
val id = (node \
val text = (node \
val source = (node \
val truncated = (node \
val inReplyToStatusId =
if ((node \
Some((node \
else
None
val inReplyToUserId =
if ((node \
Some((node \
else
None
val favorited = (node \
val user = User
}
}
}
其中最強大的一處是
好奇和細心的讀者會注意到在 Status 及其內嵌 User 的 fromXml 方法中
注意 Status 內部的兩個成員如何使用 Option[T] 類型;這是因為這些元素通常排除在 Status 消息外部
現在已經可以輕而易舉地使用公共時間軸
清單
@Test def simplePublicFeedPullAndDeserialize =
{
val publicFeedURL =
// HttpClient API
val client = new HttpClient()
val method = new GetMethod(publicFeedURL)
method
new DefaultHttpMethodRetryHandler(
val statusCode = client
val responseBody = new String(method
val responseXML = scala
val statuses = responseXML \\
for (n <
{
val s = Status
System
}
}
顯然
將所有這些結合到 Scitter 單一實例中相當簡單
清單
package com
{
import mons
import scala
object Scitter
{
//
/**
* Query the public timeline for the most recent statuses
*
* Twitter docs say:
* public_timeline
* Returns the
* a custom user icon
* public timeline is cached for
* that is a waste of resources
* URL: _timeline
* Formats: xml
* Method(s): GET
* API limit: Not applicable
* Returns: list of status elements
*/
def publicTimeline : List[Status] =
{
import llection
val client = new HttpClient()
val method =
new GetMethod(
method
new DefaultHttpMethodRetryHandler(
client
val statusLine = method
if (statusLine
{
val responseXML =
XML
val statusListBuffer = new ListBuffer[Status]
for (n <
statusListBuffer += (Status
statusListBuffer
}
else
{
Nil
}
}
}
}
在實現功能全面的 Twiter 客戶機之前
結束語
構建 Scitter 庫的工作進展順利;目前
注意
這並不是說我們在此處提供的設計是解決問題最好的方法
現在
From:http://tw.wingwit.com/Article/program/Java/Javascript/201311/25296.html