C84の3日目に「ななかINSIDE PRESS vol.3」を出します(東ぺ-15a)

f:id:mtgto:20130810101248j:plain:w300

いよいよ今日から夏コミですね!なんだか気温がとても高くなりそうとのことで、参加者の方々はくれぐれも温度調節、水分補給を怠らず楽しく参加してきてくださいね。家に帰るまでがコミケです。

さて、私がコミケに出展側で参加するのもこれで3回目。前回、前々回に続いてプログラミングメインのよもやま話がどばどば詰まった「ななかINSIDE PRESS vol.3」を頒布します。 執筆者8人で128ページときりの良い数字になっております(お値段は600円ときりがよくありませんね)。かなり分厚い(背表紙の幅が1cmくらい?)ので近くを通られましたら、お手にとって確かめていただければと思います。 第7開発セクションのサイトにはサンプルページも用意してしますので興味がある方はそちらもご覧くださいませ。

本の内容

  • AWS実践入門 -AWSの本当のところ @con_mame
    • 業務でAWSを使用しているid:con_mameによる、使用時の注意ポイントや性能測定です。 Let's インスタンスガチャ (※有料です)!
  • よりよい設計手法でSAN値を守る More Better Rails @rosylilly
    • Railsのプロジェクトが大きくなるときに発生する「モデルの命名」「モデルの肥大化」とどう立ち向かうかというRails初学者の私には耳の痛くなる話です。
  • coffeescriptで作るcoffeebotで優雅なコーヒーブレイク @saiten
    • 「それ名前駆動開発」だろって言ってしまうタイトルですが、中身はコーヒーメーカーにコーヒーが残っているかどうかを監視してくれるボットを作った話です。個人的に一番好きな記事です。
  • Render Scriptでサクサク動くAndroidアプリを @binzume
    • RenderScriptというのはアプリからネイティブコードを実行する仕組みです。それNDKでいいじゃん?と思うかもしれませんが、CPU非依存で書けるのでARM以外も対象とするときには楽ができるかも。
  • できる!たのしい!週末iPhoneアプリ開発 @kenmaz
    • 10本のiOSアプリをリリースしているid:kenmazによるアプリ開発 およびリリース後にやること が書かれています。普通の開発ノウハウでは語られない(とおもう)アプリの売上の話なんかも載ってたりします。
  • 知識に差が出る!? Coureutils大全#2 @tboffice
    • lsやcatといった日常生活に欠かせないコマンドたちのご紹介。こんなコマンドあるんだへー、とお手持ちのへぇボタンを押しながらパラパラ読むのが多分正しいかと。前号からの続きですが、これだけ読んでも問題ないです。
  • Asteriskで作る個人認証システム @mtgto
    • 私です。最近増えてきている「二段階認証」を個人でもやるための方法を音声電話、SMSの2つで書きました。
  • 88888の多い動画は!? ニコニコ動画のデータを分析してみよう @shibacow

ということで

8月12日(月)、東ぺ-15aでお待ちしております。私は朝頃サークルブースにいると思いますのでよろしくお願いします。既刊も持っていくよー。

DashでJava SE 7 日本語ドキュメントに対応させてみた

今年の5月にJava SE 7のAPIドキュメントをはじめとした、Java SE 7のドキュメントの日本語版が公開されました(Java SE7API日本語版提供開始Java SE ドキュメント)。

バージョン6までのように「Java 7 HashMap」のように検索して(Googleなら「日本語のページを検索」に制限すると最初に出てくるようになります)Webブラウザで見るのでももちろんいいのですが、MacユーザとしてはやはりDashで見たいわけです。 ちなみにDashというのはMac/iOSなどのAPIドキュメントをキーワードからインクリメンタルサーチで高速に見ることができるツールで、私は重宝しています。ダウンロードすることでScala, Ruby, Python, Node.jsなどにも対応することができます。ちなみに無料でも使えますが、フルバージョンを購入してないと検索時にたまに数秒待たされるとのことだったので、速攻でポチりました。

DashでJava 7 SE API日本語版を対応できるようにするのにはちょっと面倒くさかったのでやり方をメモっておきます。なおJava APIドキュメントのライセンスで、インターネットでの再配布は許可されていないため、Docsetの再配布をする予定はありません。予めご了承ください。

ドキュメントの取得

いくつかキーワードを変えて検索してみたのですが、どうやらJava SE 7の日本語ドキュメントはzip書庫などでまとめて取得することができないようで、仕方なくスクレイピングで取ってくることにしました。wgetで再帰ダウンロードでもいいのかもしれませんが、リンクされてないページがあると嫌だったので英語版ドキュメントをunzipしたもののファイル構造から日本語ドキュメントをダウンロードするという方法を取りました。

Javadocをdocsetに変換するツールの修正

JavadocをDash用のDocsetに変換するツールがGitHubで公開されていたので取ってきます。

https://github.com/Kapeli/javadocset

ですがこれをそのまま使うと変換中に大量の警告が出力され、Dashに取り込んでもクラスやインターフェイスがほとんど読み込めません。 これはこのツールが英語版ドキュメントの書式にのみ対応しているため、クラス名などを取得するためのキーワードが違う日本語ドキュメントは正しく処理ができないんですね。

ということで、日本語ドキュメントに対応できるようにしたForkをGitHubに用意しました。

https://github.com/mtgto/javadocset/tree/japanese

英語版にはない、表現の微妙な差にハマりました(「〜 内のクラス」と「〜 のクラス」とか)。 使用上の注意として、masterブランチは上記の修正を入れてないので、もし使おうとする人はjapaneseブランチからビルドして使ってください。

Docsetに変換

あとはDocsetに変換してDashに取り込むだけです。 javadocsetをXcodeでビルドして、

javadocset Java /path/to/api

と実行するとJava.docsetが生成されます(/path/to/apiは日本語ドキュメント内のapiフォルダへのパスを入れてください)。 これをDashにインストールすればJava 7のドキュメントが検索できるようになっているはずです。英語版Javaドキュメントがすでにインストールされていて、もう不要でしたら削除しておきましょう。

終わりに

以上でJava SE 7日本語ドキュメントをDashで引くための方法についての説明は終わりです。

参考リンクにも入れましたが、Rubyの日本語リファレンスマニュアルをDash用に対応してくれている人がいるので、よろしければそちらもチェックしてみてください。

参考

scala irc botでのbotの作り方

はじめに

この記事は、私がGitHubで公開しているscala-irc-botというプログラムの使い方とボットの書き方のチュートリアルです。 ScalaIRCボットを書きたいという需要が非常に少ない目的のために書きました。

目次

  • scala irc botの紹介
  • 使い方(デプロイ方法)
  • プラグインの作成方法
  • 設定なし
  • 設定あり

scala irc botの紹介

scala irc botってなんですか

https://github.com/scala-irc-bot

私がScalairc botを書けるように作成しているプログラムです。 実際にIRCに接続するのにはPircBotというJava製のIRCクライアントを使用しています。

現在の最新バージョンは0.2.0-SNAPSHOTです。バージョンは大きく設計が変わるときに更新されます。プラグインに影響のない修正ではバージョンを上げません(mavenリポジトリの更新が面倒だからです)。

現在の使い方は sbt run によって起動させるのを推奨しています。 性質的に常時起動しておくプログラムなのでデーモンのように使うほうがいいのかなあ。。。

irc botってなんですか

IRCでの誰かの発言などのイベントなどに反応して発言したり作業したり愛したり恋したりしてくれるプログラムです。 ときには喧嘩して殴りあったりするけれど、でもそんなお前のこと、そんなに嫌いじゃないぜ?っていう人格を持っています。

ircってなんですか

RFC1459

デプロイ方法

次のものを用意してください。

git clone

本体のコードはgithubで管理しているので、cloneで持ってきます。

git clone https://github.com/scala-irc-bot/scala-irc-bot.git

設定ファイルの記述

cloneしたディレクトリに移動し、configディレクトリの中に、Config.scalaというファイルを作成します。

Config.scala

import net.mtgto.irc.Config
import net.mtgto.irc.config.BotConfig
new Config {
  val hostname = "irc.example.com" // IRCサーバの名前
  val port = 6667 // IRCサーバのポート
  val password = None // 必要ならSome("password")のように記述する
  val encoding = "utf-8" // 使用するエンコーディング
  val messageDelay = 2000 // メッセージ間の遅延(ミリ秒)
  val timerDelay = 60000 // タイマーイベント間隔(ミリ秒)
  val nickname = "scala-irc-bot" // ボットのニックネーム
  val username = "scala-irc-bot" // ボットのログイン名
  val realname = "mtgto@example.com" // ボットのリアルネーム
  // channels
  val channels = Array("#mtgto", "#test") // JOINさせたいチャンネル
  // bots
  val bots = Array[(String, Option[BotConfig])](
    // ここにプラグインの設定を記述する
  )
}

起動する

コンソールからsbtで起動します。

sbt run

(初回のみ)コンパイルが実行され、設定ファイルやネットワークなどに問題が起きなければ、しばらく経つとIRCサーバにscala irc botが接続してきます。

ですが今のままではボットプラグインを何も入れてないので、何もしてくれません。 コンソール画面にexitと入力して終了させましょう。

プラグインの作成方法 (シンプル)

ここからはまずはシンプルなプラグインを作成していきます。 ここでいうシンプルなプラグインというのは、

  1. 設定ファイルでボットプラグインごとのオプションを設定できない

っていう前提のものです。下の方にオプションを使う方法も載せてあります。

Scala + sbtで作る

今回作る環境は、

  • sbt (バージョンはユーザの環境に依る)
  • Scala 2.10.0
  • PircBot 1.8.0

です。

最小構成では build.sbt Echobot.scala の2ファイルで構成されます。

echobot/build.sbt

// -*- scala -*-
name := "EchoBot"

organization := "net.mtgto"

version := "0.1.0-SNAPSHOT"

scalaVersion := "2.10.0"

resolvers += "mtgto repos" at "http://scala-irc-bot.github.com/scala-irc-bot/maven/"

libraryDependencies := Seq(
  "net.mtgto" %% "scala-irc-bot" % "0.2.0-SNAPSHOT" % "provided"
)

scalacOptions ++= Seq("-deprecation", "-unchecked", "-encoding", "UTF8")

echobot/Echobot.scala

package net.mtgto

import net.mtgto.irc.{Bot, Client}
import net.mtgto.irc.event._

/**
 * 受け取ったメッセージをNOTICEで送り返すボット
 */
class Echobot extends Bot {
  /**
   * 誰かがPRIVMSGを書いた時に通知される
   */
  override def onMessage(client: Client, message: Message) = {
    // メッセージが送信されたチャンネルに同じメッセージをNOTICEで送り返す
    client.sendNotice(message.channel, message.text)
  }
}

ファイルを置いたら、

sbt package

と実行すると、 target/scala-2.10.0/echobot_2.10.0-0.1.0-SNAPSHOT.jar が作成されます。

このjarファイルがコンパイルされたプラグインです。

プラグインをインストール

まず、作成した echobot_2.10.0-0.1.0-SNAPSHOT.jarscala-irc-bot/botsフォルダの中に置いてください。 次に config/Config.scala にボットプラグインの設定を記述します。

  val bots = Array[(String, Option[BotConfig])](
    // ここにプラグインの設定を記述する
    ("net.mtgto.Echobot", None)
  )

sbt run すると画像のようにこちらの発言をNOTICEで繰り返してくれるようになります。

f:id:mtgto:20121216223101p:plain

botテンプレートを使う (g8)

giter8用のテンプレートを作ってあるので、これを使うこともできます。

g8 scala-irc-bot/bot
version [0.1.0-SNAPSHOT]:
organization [com.example]: net.mtgto
name [IRC Bot Project]: EchoBot

こちらは先ほどの手順で作成した時に加えて、テスト用にSpecs2、ビルド用にsbt-assemblyの設定をしてあります。 慣れてきた場合や他ライブラリに依存するプラグインを作るときにはこちらをおすすめします。

プラグインの作成方法 (設定可能)

設定ファイルでボットプラグインごとのオプションを設定することができます。

設定ファイルに書いたオプションを読み込むには、net.mtgto.irc.config.BotConfigを実装したクラスを作り、そのオブジェクトをプラグインのメインクラスのコンストラクタにもたせてあげればよいです。

実例を示してみます。今回は設定ファイルに書いたキーワードが書かれた時だけ反応するようにしてみましょう。 KeywordEchobotを作成します。

KeywordEchobot.scala

package net.mtgto

import net.mtgto.irc.{Bot, Client}
import net.mtgto.irc.event._
import net.mtgto.irc.config.BotConfig

/**
 * BotConfigを実装したクラスを定義する必要がある。
 */
case class KeywordEchobotConfig(
  val keywords: Seq[String]
) extends BotConfig

/**
 * 特定のメッセージをNOTICEで送り返すボット
 *
 * コンストラクタでEchobotConfigを受け取る
 */
class KeywordEchobot(val config: KeywordEchobotConfig) extends Bot {
  /**
   * 誰かがPRIVMSGを書いた時に通知される
   */
  override def onMessage(client: Client, message: Message) = {
    if (config.keywords.contains(message.text)) {
      // メッセージが送信されたチャンネルに同じメッセージをNOTICEで送り返す
      client.sendNotice(message.channel, message.text)
    }
  }
}

これを先ほどと同じように sbt package でビルドして、target/scala-2.9.2/keywordechobot_2.10.0-0.1.0-SNAPSHOT.jarscala-irc-bot/bots にコピーします。

あとは config/Config.scala に設定を書いてあげます。

val bots = Array[(String, Option[BotConfig])](
  ("net.mtgto.Echobot", Some(net.mtgto.KeywordEchobotConfig(Seq("こんばんは", "おはようございます"))))
)

Seqに好きな言葉を列挙してあげます。

あとは sbt run すると、設定ファイルにある言葉が書かれた時にだけ応答してくれるようになります。

f:id:mtgto:20121216223145p:plain

まとめ

簡単でしたが、scala irc botの導入方法とプラグインの作成方法を書きました。 多分ここまで読んでいる方はいらっしゃらないと思うのですが、さらにプラグインを作ってみたいという奇特な方がいらっしゃるようでしたらこの上なく幸せです。 まだサンプルプログラムが少ないのですが、私も幾つかプラグインを作っているのでよろしければそれを見てみてください。

https://github.com/scala-irc-bot

CocoaPodsでGHUnit + OCMockなiOS開発環境を構築する

なにこれ

新しいプロジェクト作るたびにいつも構築手順を忘れてるのでメモ。 おまけでコンソールからのテスト実行(Jenkinsとの統合が目的)のやり方も書いた。

対象者

  • Objective-CによるiOS開発者
  • CocoaPodsを知っている・使いたい
  • GHUnitを知っている・使いたい
  • (JenkinsでCIしたい)←必須じゃない

検証環境

2012年12月9日時点で最新のもの。

手順

1. Xcodeでプロジェクト作成

Unit TestはGHUnitを使うので不要。 このドキュメントでは MyProject とする。

2. 新しいターゲット作成

プロジェクト設定を開いて、テスト実行用のターゲットを作成する。 ターゲットの種類はiOSのEmpty Application、名前は Tests にしておく。 名前は変更してもいいけど、以下の説明で Tests と出てきたら自分で読み替えること。

自動生成されるファイルのうち、次のものだけ残して全部消す(ファイルごと)

  • Tests-Info.plist
  • InfoPlist.strings
  • main.m
  • Tests-Prefix.pch

3. Podfile作成

MyProject.xcodeproj と同じ所に Podfile を作成する。

platform :ios, '5.0'

pod 'AFNetworking',  '~> 1.0.1'
pod 'CocoaLumberjack', '~> 1.6'

target :Tests, :exclusive => true do
  pod 'GHUnitIOS', '~> 0.5.5'
  pod 'OCMock', '~> 2.0.1'
end

一行目でiOS 5.0以上を指定しているが、実際には作成するプロジェクトに合わせて指定する。 AFNetworkingとCocoaLumberjackは実際に作ったPodfileから取ってきただけのただのサンプル。実際に必要な物を指定して欲しい。 このとき target :Tests を2.で作ったターゲット名にするのを忘れないこと。

終わったらコンソールからworkspaceを作成してもらう。

pod install

4. xcodeproj閉じてxcworkspace開く

両方同じxcodeprojを参照するのでわけわからないことになる前に xcodeproj を閉じる。

5. ターゲット TestsBuild Settings いじる

Other Linker Flags-ObjC -all_load を追加する。 俺の環境では -ObjC はすでに追加されていた。

6. main.m いじる

//
//  main.m
//  Tests
//
//  Created by <ユーザ名> on 12/8/12.
//  Copyright (c) 2012 <組織名>. All rights reserved.
//

#import <UIKit/UIKit.h>

int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, @"GHUnitIOSAppDelegate");
    }
}

ここまでやってiPhone Simulatorで起動するとGHUnitのテスト実行画面が表示される。

7. ユニットテスト作ってみる

GHUnit

ここまでうまくいってるか確認するために一個テスト作ってみる。 Testsに一個Objective-Cのクラスを作る。 GHUnitのガイド にあるコードのままだとコンパイルが通らないのでちょいと手直し。

MyTest.h

#import <GHUnitIOS/GHUnit.h>

@interface MyTest : GHTestCase

@end

MyTest.m

#import "MyTest.h"

@implementation MyTest

- (void)testStrings {
    NSString *string1 = @"a string";
    GHTestLog(@"I can log to the GHUnit test console: %@", string1);

    // Assert string1 is not NULL, with no custom error description
    GHAssertNotNil(string1, nil);

    // Assert equal objects, add custom error description
    NSString *string2 = @"a string";
    GHAssertEqualObjects(string1, string2, @"A custom error message. string1 should be equal to: %@.", string2);
}

@end

修正前はGHAssertNotNULLを使ってたのでGHAssertNotNilに変えた。

もっかいiPhone Simulator起動してみてテストできることを確認する。

OCMockも確認する

MyMockTest.h

#import <GHUnitIOS/GHUnit.h>
#import <OCMock/OCMock.h>

@interface MyTest : GHTestCase

@end

MyMockTest.m

#import "MyMockTest.h"

@implementation MyMockTest

- (void)testMock {
    id mock = [OCMockObject mockForClass:[NSString class]];
    [[[mock stub] andReturnValue:OCMOCK_VALUE((NSUInteger){100})] length];

    GHAssertEquals(100U, [mock length], @"ocmock works");
}

@end

iPhone Simulator起動してみてテストできることを確認する。

8. コンソールからテスト実行してみる

Jenkins用なんで興味ない人はスルー。 基本は GHUnitのガイド 通り。

i. シェルスクリプトを取ってくる

xcodeproj, xcworkspaceがあるところで以下を実行。

wget https://raw.github.com/gabriel/gh-unit/master/Scripts/RunTests.sh
wget https://raw.github.com/gabriel/gh-unit/master/Scripts/RunIPhoneSecurityd.sh

ii. テスト実行時にスクリプトを実行するようにする

プロジェクト設定内のTestsBuild Phasesを開き、Add Build Phase -> Add Run Scriptを選択。 入力欄にsh RunTests.shと入れる。

iii. Makefile

GHUnitのドキュメントにはworkspaceのときの例がないのでそのままだと動かない。 適当に作ってみる。

Makefile

clean:
	-rm -rf build/*

test:
	GHUNIT_CLI=1 xcodebuild ONLY_ACTIVE_ARCH=NO -workspace <xcworkspaceファイルへのパス> -scheme Tests -configuration Debug -sdk iphonesimulator build

ワークスペースへのパスに書き換えるのを忘れないこと。 連続空白に見えるところはタブ文字にすること。

iv. (Base SDKが6.0のときのみ) main.mの修正

2012年12月9日現在、iOS SDKが6.0だと、コンソールから動かせない問題がある(GHUnitのIssue at GitHub)。 暫定措置だが一番下のgfxさんのコメント通りにmain.mを書き換えるとコンソールから動かせるようになる。

v. テストを実行する

make test

echo $?して0だったら、おめでとう。すべてのテストをパスしたぞ!

9. Jenkinsからテスト実行

まず前章で作ったRunTests.sh, RunIPhoneSecurityd.sh, Makefileをgit commitしておく。 公開前提のプロジェクトだと、git cloneしたユーザにpod installを実行させるのでxcworkspaceをソースコードリポジトリ管理しないと思う。

なのでその流儀に則り、Jenkinsでもpod installすることにする。

i. ジョブを作る

名前はMyProjectにしてみた。フリースタイル・プロジェクトを選択。

ii. 設定

ソースコード管理システムでソースの位置を指定する。 俺はローカルホストにJenkinsを立ててるので、GitのRepository URLがfile:///からはじまる。

ビルドに シェルの実行 を追加する。

WORKSPACE_FILE=MyProject.xcworkspace
if [ ! -d "$WORKSPACE_FILE" ]; then
  pod install
  open -R "$WORKSPACE_FILE"
  echo "We need to init the workspace file. Please try re-run test after you open workspace file."
  exit 1
fi
make clean && WRITE_JUNIT_XML=YES JUNIT_XML_DIR=tmp/test-results make test

最初のビルドではxcworkspaceの初期化が終わっていないため、 xcodebuild: error: The workspace 'MyProject' does not contain a scheme named 'Tests'. と失敗してしまう。

少し悩んだが、ユーザにXcodeでxcworkspaceを開かせることにした。 一度Xcodeで開いてしまえばもうこの作業はやらなくてOKだ。

open -R <ファイルパス> はFinderでファイルを表示するコマンド。

ビルド後の処理 から JUnitテスト結果の集計 を選び、tmp/test-results/*.xmlと指定する。

ビルドしてみると一回目は失敗すると思う。 xcworkspaceがFinderで表示されていると思うので、ダブルクリックして初期化してから再度ビルドすればテストが実行されるはずだ。

このへんはバッドノウハウなんで、もっといい方法あるって人は教えて欲しい。

サンプル

https://github.com/mtgto/CocoaPodsSample

ここまでの説明があってるかの確認のために作ってみた。

参考

全部英語のページ。