twitteR に pull request を送ってみた ~R パッケージの修正と動作確認方法~

以前こんな記事を書きました。
OAuthに”対応”したtwitteRを試してみた - あらびき日記

あの後に Twitter API v1.1 に “対応した” こともあって、なんというか、まぁいろいろありました。twitteR 1.1.0 の話です。

ドキュメントに従って全ての機能を試してみただけでどうしてこうなるんでしょうか・・・。

まぁ内容の詳細は置いといて、パッケージを修正する際にどうやって動作確認したかについてメモがてらサクッとご紹介しようと思います。

insertSource による修正

この関数は正にパッケージの修正のために導入された関数です。いちいち修正版を再インストールして動作確認なんてやってられませんよね。
っで、twitteR も ROAuth も厄介なのが R5 書かれていることで、普通にファイルを指定するだけだとエラーになって変更されません。

> insertSource("~/repos/ROAuth/R/ROAuth.R", package = "ROAuth")
Error in OAuthFactory$accessors(names(OAuthFactory$fields())) : 
  The definition of class OAuth in package ROAuth is locked, fields may not be modified
In addition: Warning message:
In .recacheSubclasses(def@className, def, doSubclasses, env) :
  undefined subclass "OAuth" of class "refObject"; definition not updated

このエラーに対処するには OAuthFactory$accessors の行をコメントアウトするのが手っ取り早いです。
OAuth クラスの定義がロックされているのに OAuthFactory$accessors によって getter/setter を定義しようとしているのがエラーの原因です。

コメントアウトして再実行すると次のような結果になります。

> insertSource("~/repos/ROAuth/R/ROAuth.R", package = "ROAuth")
Non-function objects aren't currently inserted (not traceable): .__global__, .packageName, .requireCachedGenerics, OAuthFactory
Modified functions inserted through trace(): oauthPOST

メッセージから ROAuth:::oauthPOST が変更されたことが伺えます。

> ROAuth:::oauthPOST
Object of class "functionWithTrace", from source
function (url, consumerKey, consumerSecret, oauthKey, oauthSecret, 
    params = character(), customHeader = NULL, curl = getCurlHandle(), 
    signMethod = "HMAC", handshakeComplete = TRUE, ...) 
{
    if (is.null(curl)) 
        curl <- getCurlHandle()
    auth <- signRequest(url, params, consumerKey, consumerSecret, 
        oauthKey = oauthKey, oauthSecret = oauthSecret, httpMethod = "POST", 
        signMethod = signMethod, handshakeComplete = handshakeComplete)
    opts <- list(...)
    postForm(url, .params = c(params, lapply(auth, I)), curl = curl, 
        .opts = opts, style = "POST")
}
<environment: namespace:ROAuth>

## (to see original from package, look at object@original)

insertSource では trace の仕組みを使っているので untrace で元に戻すことができます。

> untrace(ROAuth:::oauthPOST)
Untracing function "oauthPOST" in package "ROAuth (not-exported)"
> ROAuth:::oauthPOST
function (url, consumerKey, consumerSecret, oauthKey, oauthSecret, 
    params = character(), customHeader = NULL, curl = getCurlHandle(), 
    signMethod = "HMAC", handshakeComplete = TRUE, ...) 
{
    if (is.null(curl)) 
        curl <- getCurlHandle()
    auth <- signRequest(url, params, consumerKey, consumerSecret, 
        oauthKey = oauthKey, oauthSecret = oauthSecret, httpMethod = "POST", 
        signMethod = signMethod, handshakeComplete = handshakeComplete)
    opts <- list(...)
    if (length(params) == 0) {
        reader <- dynCurlReader(curl, baseURL = url, verbose = FALSE)
        fields <- paste(names(auth), sapply(auth, curlPercentEncode), 
            sep = "=", collapse = "&")
        curlPerform(curl = curl, URL = url, postfields = fields, 
            writefunction = reader$update, ...)
        reader$value()
    }
    else postForm(url, .params = c(params, lapply(auth, I)), 
        curl = curl, .opts = opts, style = "POST")
}
<environment: namespace:ROAuth>

trace による修正

trace を使えばたいていの場合どうにでもなります。
例えば insertSource だと関数やクラス定義が変わるだけなので、シングルトン的な twInterfaceObj のメソッドを変更することはできません。
なので、twInterfaceObj の doAPICall を変更しようと思うと trace メソッドによって直接書き換えるしかありません。

twitteR:::twInterfaceObj$trace(doAPICall, edit = TRUE)

元に戻すには次のように untrace メソッドを呼びます。

twitteR:::twInterfaceObj$untrace(doAPICall)

変態的なやり方として次のように envir に twitteR:::twInterfaceObj を指定した上で body<- や edit によって書き換えることも可能です。オススメはしないですが。

evalq(body(doAPICall)[[1]] <- "hoge" , twitteR:::twInterfaceObj)

こんな感じで修正&動作確認を繰り返して pull request を送りました。
みなさんもドンドン pull request 送りましょう!!