jython + twitter4j でボット作成作業メモ

メモ書きをメモメモ残すだけのブログ

vim】コマンドの検索

「q:」コマンドでコマンドの履歴を閲覧できる
履歴を抜けるには「:q」ファイル終了と同じ
履歴も普通のファイルみたいにvimキーバインドで操作できる。
カーソル移動、検索、rによる置換まで確認。
エンターを押すとその時にカーソルの乗っていたコマンドが実行される。
置換で入力を間違えた時に便利でした。
http://nanasi.jp/articles/howto/editing/use-command-history.html

vim】自動補完

「Ctrl + p」で補完対象を表示
「Ctrl + n」で表示した候補から決定
http://www.webhtm.net/vim/inputsupport.htm

【twitter4j】使い方

twitter4jのAccessTokenを出力する方法
http://d.hatena.ne.jp/ymuto109/20110613/1307949741

java】オブジェクトの出力(シリアライズ)

jythonだと、読み込み時にはこの方法じゃダメだって分かった。後述
http://www.javaroad.jp/java_io7.htm

jython】例外処理の方法

try:
	例外を起こしそうな処理
except <対処するクラス名>:
	例外処理

だそうである。
http://d.hatena.ne.jp/yumimue/20071211/1197373464

jython】キャストの仕方

こんな感じにするらしい。

cnt = 10
cnt_str = str(cnt)

これってjythonの適当なクラスにも有効なんだろうか?
http://osima.jp/blog/python-cast-to-string/index.html
→ なんかjythonだとキャストいらないっぽい。

jythonシリアライズしたオブジェクトの読み込み

出力は普通にObjectOutputStreamで良いらしんだけど、
入力時にはObjectInputStreamじゃなくて
PythonObjectInputStreamを使わないといけないんだって。
http://onlamp.com/pub/a/python/2002/04/11/jythontips.html?page=2

jython】PythonObjectInputStream

シリアライズしたオブジェクトを読み込むときにエラー。
どこかで見つけた例文通りに以下のように記述

import org.python.util as util
...
ois = util.PythonObjectInputStream(...

するとjava.utilと衝突してクラスの読み込みに失敗した感じ

それならクラスパス全部指定してみると、

import org.python.util.PythonOjbectInputStream
...
ois = PythonObjectInputStream(...

そしたら、グローバル変数としてみなされてしまったらしい

よくわからないから衝突しないようにしてみた

import org.python.util as pyutil
...
ois = pyutil.PythonObjectInputStream(...

にしたらいけた気がする。
よくわかんない。

jython】1行を複数行に分ける

行末に \ を付けることで分離ができる。
http://www.glamenv-septzen.net/view/185

【twitter4j】

検索APIの使い方
http://d.hatena.ne.jp/AjakuPanic/20101027/1288187872

【twitter4j】

出力が文字化けってる
→ 表示環境の問題でした。
export LANG=ja_JP.UTF-8 で解決
http://d.hatena.ne.jp/ymuto109/20110930/1317370087

【twitter4j】

っていうかAPI対応表とJavaDoc読め
TwitterAPIとの対応表 http://twitter4j.org/ja/api-support.html
(このブログを書いてる時点で)最新版JavaDoc http://twitter4j.org/ja/javadoc/index.html

Maven勉強

Maven2を利用している割にあまり理解していないので勉強してみる。
というか、資料をあさってみる

入門用のスライドを作成している方がいた。

http://d.hatena.ne.jp/daisuke-m/20071220/1198131596
特にmavenのデフォルト動作について詳しく書かれていた記憶。

開発環境や本番環境で設定が変わる場合にはprofileを使いましょう

と書いてあったページ
http://d.hatena.ne.jp/cynipe/20110227/1298805907

マルチモジュールプロジェクトの作り方について手順が紹介されてたページ

http://www.techscore.com/tech/Java/ApacheJakarta/Maven/3-6/#maven-3-4
ただ、手順どおりやるとエラーが吐き出された。
親プロジェクト作る → pomファイルのパッケージング書き換え "jar" -> "pom" → 子プロジェクト作る
が正しい流れらしい。

子供作ると親のpomに

  <modules>
    <module>child1</module>
    <module>child2</module>
  </modules>

が追加されてる。

子供にも

  <parent>
    <artifactId>parent</artifactId>
    <groupId>test.polistes.mmp</groupId>
    <version>1.0-SNAPSHOT</version>
  </parent>

が記載されてる。

Maven利用時のTipsがまとまってたページ

http://d.hatena.ne.jp/cynipe/20101128/1290943804
親プロジェクトには共通するパッケージを記述するといいって。

erlang-oauthからTwitterに投稿するだけのプログラムがかけたよ

erlang-oauthを利用する。

動いて満足したので、リファクタリングとかしてない。

プログラム

-module(helloworld).
-export([hello/0]).

hello() ->
        ssl:start(),
        Consumer = {"<consumer key>", "consumer secret", hmac_sha1},
        RequestTokenURL = "https://api.twitter.com/oauth/request_token",
        {ok, RequestTokenResponse} = oauth:get(RequestTokenURL, [], Consumer),
        RequestTokenParams = oauth:params_decode(RequestTokenResponse),
        RequestToken = oauth:token(RequestTokenParams),
        RequestTokenSecret = oauth:token_secret(RequestTokenParams),
        {_, _, OauthToken} = RequestTokenResponse,
        AuthorizeURL = "https://api.twitter.com/oauth/authorize",
        io:format("confirm : ~s?~s~n", [AuthorizeURL, OauthToken]),
        io:format("input PIN : "),
        UserInput = io:get_line(standard_io,''),
        Pin = string:substr(UserInput, 1, length(UserInput)-1),
        AccessTokenURL = "https://api.twitter.com/oauth/access_token",
        {ok, AccessTokenResponse} = oauth:get(AccessTokenURL, [{"oauth_verifier", Pin}], Consumer, RequestToken, RequestTokenSecret),
        AccessTokenParams = oauth:params_decode(AccessTokenResponse),
        AccessToken = oauth:token(AccessTokenParams),
        AccessTokenSecret = oauth:token_secret(AccessTokenParams),
        URL = "http://api.twitter.com/1/statuses/update.json",
        {ok, Response} = oauth:post(URL, [{"status", "hello erlang-oauth world!"}], Consumer, AccessToken, AccessTokenSecret).

実行する

$ erl -pa ebin -s crypto -s inets
1> c(helloworld).
WARNINGが出るけど無視。
2> helloworld:hello().
confirm : https://api.twitter.com/oauth/authorize?oauth_token=hogehogehogehoge
↑ URLが表示されるので、アクセスして認証する。
input PIN :
↑ 上のURLで認証するとPINが表示されるので入力する
何か表示される。文字列の中に「200, "OK"」があればOK

mockitoが動かない!(とりあえず動いた編)

前回はmockitoが動かない問題だけを紹介したので、とりあえずの解決編。
問題は残ってるんだけど。。。


方針としては、モックオブジェクトのwriteメソッドを偽装してメソッドが呼び出された時の引数を別のオブジェクトに格納しておくよ。
返り値のないメソッドを偽装するときにはdoAnswerを利用する。

	final List<Map<Text, Text>> realInvocation = new ArrayList<Map<Text,Text>>();

	doAnswer(new Answer<Object>() {
		public Object answer(InvocationOnMock invocation) throws Throwable {
			Text invocedKey = (Text) invocation.getArguments()[0];
			Text invocedValue = (Text) invocation.getArguments()[1];
			
			Text key = new Text(Arrays.copyOfRange(invocedKey.getBytes(), 0, invocedKey.getLength()));
			Text value = new Text(Arrays.copyOfRange(invocedValue.getBytes(), 0, invocedValue.getLength()));
			
			Map<Text, Text> savedInvocation = new HashMap<Text, Text>();
			savedInvocation.put(key, value);
			realInvocation.add(savedInvocation);
			return null;
		}
	}).when(mockContext).write(any(Text.class), any(Text.class));

こんな感じでwriteメソッドが呼び出された時に、引数をちゃんとコピーして、自分の用意したListの中に格納する。
Listに格納しちゃうので、確認方法も修正

	assertEquals(realInvocation.get(0).get(new Text("0")), new Text("0"));
	assertEquals(realInvocation.get(1).get(new Text("1")), new Text("1"));
	assertEquals(realInvocation.get(2).get(new Text("2")), new Text("2"));
	assertEquals(realInvocation.get(3).get(new Text("3")), new Text("3"));
	assertEquals(realInvocation.get(4).get(new Text("4")), new Text("4"));

コレでテストが通るようになる。

問題点としては、モックオブジェクトの呼び出しに対しては確認を行ってないので、verifyNoMoreInteractionsを実行するとこけてしまうこと。
自分でオブジェクトを用意して格納するのではなく、mockitoの保存しているInvocationを上書きできれば良いんだけれど、まだよくわかってない。

mockitoが動かない!

Hadoopを使って真面目な集計をするので、テストの真面目に書くことになりました。
mockitoつかってテストするよ(`・ω・´)

適当なMapper

package mapreduceWithMockito;

import java.io.IOException;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class SampleMapper extends Mapper<Object, Text, Text, Text>{
	private Text mapkey = new Text();
	private Text mapvalue = new Text();
	
	protected void map(Object key, Text value, Context context)
			throws IOException ,InterruptedException {
		for (int i = 0 ; i < 5 ; i++) {
			mapkey.set(Integer.toString(i));
			mapvalue.set(Integer.toString(i));
			
			// 1つのMapperで、同じWritableオブジェクトを利用して複数のレコードを出力
			context.write(mapkey, mapvalue);
		}
	}

}

コメントにあるとおり、同じTextを使いまわして出力する。
オブジェクト生成のオーバーヘッドがなくなるとか何とか。
本当かどうかは知らない。

Mapper用の適当なテストコード

package mapreduceWithMockito;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import java.io.IOException;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Mapper.Context;
import org.mockito.internal.debugging.MockitoDebuggerImpl;
import org.testng.annotations.Test;

@Test
public class SampleMapperTest {
	public void mapTest() throws IOException, InterruptedException {
		Mapper<Object, Text, Text, Text>.Context mockContext = mock(Context.class);
		SampleMapper mapper = new SampleMapper();

		// key, valueは適当
		mapper.map(null, null, mockContext);

		verify(mockContext).write(new Text("0"), new Text("0"));
		verify(mockContext).write(new Text("1"), new Text("1"));
		verify(mockContext).write(new Text("2"), new Text("2"));
		verify(mockContext).write(new Text("3"), new Text("3"));
		verify(mockContext).write(new Text("4"), new Text("4"));
	}
}

TestNGを使ってるのは趣味です。


まぁ、失敗するはずもなく、

出力

===============================================
Suite1
Total tests run: 1, Failures: 1, Skips: 0
===============================================

Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.357 sec <<< FAILURE!

Results :

Failed tests: 
  mapTest(mapreduceWithMockito.SampleMapperTest)

Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

工工工エエエェェェ(´д`;)ェェェエエエ工工工
失敗するんですけど。

testng-result.xml

Argument(s) are different! Wanted:
context.write(1, 1);
-> at mapreduceWithMockito.SampleMapperTest.mapTest(SampleMapperTest.java:27)
Actual invocation has different arguments:
context.write(4, 4);
-> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)

引数が違うって怒られてる。
4, 4が呼び出されるんだったら1, 1も呼び出されるよね、JK。

確認する

		new MockitoDebuggerImpl().printInvocations(mockContext);

mapper呼び出しの跡にInvocationの一覧を出力する

確認するの出力

********************************
*** Mockito interactions log ***
********************************
context.write(4, 4);
 invoked: -> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)
context.write(4, 4);
 invoked: -> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)
context.write(4, 4);
 invoked: -> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)
context.write(4, 4);
 invoked: -> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)
context.write(4, 4);
 invoked: -> at mapreduceWithMockito.SampleMapper.map(SampleMapper.java:19)

全部おんなじ呼び出しだよ!やったねたえちゃん!!
同じTextオブジェクト使ってるからwriteした値が書き変わっちゃったらしい。
mockitoってシャローコピーしてるのか。。。

もしかしてこれって毎回オブジェクト生成しないとテスト通らないのかなー。。。

Eclipse + egit

以前構築した開発環境を初期化してしまったので、再度gitに接続するためのメモ。
前回も散々egitを罵倒したうえでひょんなタイミングでpushができてしまったので次こそ忘れないために書きだす。


egitのインストール

Eclipseにgitのプラグインをインストールする。
今回はEclipseでGitを使おう! EGitを試してみるで紹介されていたegitを利用。

  1. [help] -> [Install New Software...] でインストールウィンドウを開く
  2. 右上の[Add...]をクリック
  3. [Name]に適当な名前(egit等)を入力、[Location]にhttp://www.jgit.org/update-siteを入力し[OK]
  4. しばらくするとインストール可能なegitのビルドタイプ一覧が表示されるので、好きなものを選んでNext。今回はStableを選択
  5. 画面に従ってインストール
  6. 再起動を求められるので、Eclipseを再起動

リポジトリのチェックアウト

git上にあるリポジトリをローカルにチェックアウトする方法
公開されているリポジトリなら特にsshキーとかはいらない。

  1. デフォルトの左側に表示されている[Package Exproler]内の適当なところで右クリック -> [Import]
  2. [Git] -> [Git Repository]を選択して[Next]
  3. [URI]を入力(例:https://github.com/polistes/dezapatan)
  4. [Finish]でダウンロードを始めるんだったかな

コミット

ローカルのリポジトリに更新を反映する。
※ コミットしただけじゃgitには反映されません

  1. 更新したファイルやパッケージなど適当な個所で右クリック -> [Team] -> [Commit]

プッシュ

ローカルのリポジトリにコミットした更新分を、gitに反映させる。

sshキーの準備
  1. 適当にssh2の秘密鍵を用意する。
  2. gitに登録されていることは大前提。
  3. windowsの場合には[C:\Users\<ユーザ名>\ssh]以下にid_rsaファイルを設置する。
  4. もしくは、Eclipseの[Window] -> [Preference] -> [General] ^> [Network Connection] -> [SSH2] -> [SSH2 home]のパスを適当に編集
更新のコミット
  1. プロジェクトを右クリック
  2. [Team] -> [Push to...]
  3. [Custom URI]を選択し、[URI]を入力。(例: git+ssh://git@github.com/polistes/dezapatan.git)
  4. [Source ref]と[Destination ref]を適当に選択(例: src->HEAD dest->master)
  5. [Next]をクリック
  6. 緑色に表示がされたら更新はたぶん成功
  7. [Finish]をクリック
  8. 再度緑色に表示されたら今度こそ更新成功

感想

散々「Auth fail」が表示されると思ったらURIが違ったらしい。
アカウントは[git]
URIの一番最後につける拡張子は[.git]
にしないといけないの?


さくらサーバにnode.jsをインストールしたい(未完了)

websocket使いたい!
でもよくわからない。

途中の記録が飛んでるけど、node.jsをインストールしようとしたので、途中から途中までメモ
そして途中で挫折中

node.jsインストール

$ cd $HOME/src/node-v0.4.1
$ ./configure --prefix=$HOME/local
Checking for program g++ or c++          : /usr/bin/g++
Checking for program cpp                 : /usr/bin/cpp
Checking for program ar                  : /usr/bin/ar
Checking for program ranlib              : /usr/bin/ranlib
Checking for g++                         : ok
Checking for program gcc or cc           : /usr/bin/gcc
Checking for gcc                         : ok
Checking for library dl                  : not found
Checking for library kvm                 : yes
Checking for library execinfo            : not found
$HOME/src/node-v0.4.1/wscript:255: error: Install the libexecinfo port from /usr/ports/devel/libexecinfo.

libexecinfoがないらしい。

portsを使えばいけるらしいので、以下のサイトを頼りにportsのインストール。
Sakura共用サーバにportsをインストール

何を設定してるのかわからないけど、だいたいその通りにやった。

libexecinfoのインストールを試す

$ cd $HOME/usr/ports/devel/libexecinfo/
$ make
===>  Building for libexecinfo-1.1_3
"/usr/share/mk/bsd.compat.mk", line 35: warning: NOPROFILE is deprecated in favour of NO_PROFILE
"/usr/share/mk/bsd.own.mk", line 110: Cannot open /etc/src.conf
make: fatal errors encountered -- cannot continue
*** Error code 1

Stop in $HOME/usr/ports/devel/libexecinfo.

/etc/src.confが開けないって。

$ ls  -al /etc/src.conf
-rw-------  1 root  wheel  140 Jan 19  2010 /etc/src.conf

ルート権限持ってないからアクセス出来ない
(´・ω・`)ショボーン

誰かおらに知識を。