Skip to main content

33 posts tagged with "Scala"

View All Tags

· 3 min read

Sublime Text 2にensimeを入れてみたものの、完璧ではない。

Sublime Text 2でensimeを利用してみたが…

■ Use Sublime Text 2 for Scala Development « A Pinoy Programmer in Japanhttp://blog.hugeaim.com/2012/03/22/use-sublime-text-2-for-scala-development/
It seems that the validation is not yet perfect, so I would still use this in combination with an SBT build
こんな記述もあるわけだし、完璧ではないのは承知の上。 エラーがあるのか無いのかよくわからないこともあり、sbtも導入しておくのがよさそう。

· 8 min read

また海藻猫が騒いでいたのを見つけてしまったのが運の尽き…。 [blackbirdpie id="126122863177633793"]

Android開発はやったことがないからわからないけど、Scala + Eclipseは使っているから、簡単に手順を書いておく。

■ teaplanet/sbt-appengine-eclipse - GitHub

https://github.com/teaplanet/sbt-appengine-eclipse以下の手順で作るふたつのbuild.sbtはGitHubに置いといた。

前提

sbtとEclipseを使ってAppEngineの開発を行う。 テストにはspecs2を使うことを想定してるので、specs2がいらない場合は後述のbuild.sbtからspecs2の依存関係消してね。

こんな役割分担で使ってます。 依存関係解決:sbt エディタ:Eclipse コンパイル:Eclipse(, sbt) テスト:Eclipse(, sbt) デバッグ:Eclipse こんな構成で行います。

ライブラリの依存関係の解決はsbtに任せる。ついでにEclipseのプロジェクトファイル(.project)の生成もsbtに任せる。 Eclipseはエディタとして使いつつ、同時にコンパイルも行う。 テストしつつデバッグもEclipseで行ってる。 基本的にどちらを使ってもいいんだけど、個人的にはこうやって使ってる。

依存関係の解決をsbtがやってくれるもんだから、バージョン管理にバイナリが含まれなくて精神衛生上よい。 プロジェクトをコンパイルするにもコンソールだけで済むし(Eclipse不要)。

環境

Mac OS X 10.7.1 Scala 2.9.1 sbt 0.11.0-RC1 Eclipse Indigo

開発環境構築

sbtの入手

■ Index of ivy-snapshots/org.scala-tools.sbt/sbt-launch/0.11.0-RC1http://repo.typesafe.com/typesafe/ivy-snapshots/org.scala-tools.sbt/sbt-launch/0.11.0-RC1/コンパイルしてもいいけど、持ってきた方が楽。今はこれを使ってます。 違うバージョンでも問題ないかもしれないけど、他のバージョンを使ってたらsbteclipseが動かなかった。

sbtのインストール

入手したsbt(sbt-launch.jar)を実行するために ~/bin/sbt ファイルを作成する。
java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"
chmod u+x ~/bin/sbt
実行権限付けて、実行できるようにする。

プロジェクト作成

プロジェクトは "ws/sample" に作ることにする。
mkdir -p ws/sample cd ws/sample sbt
これでsbtで必要なファイルが作成され、sbtのプロンプトが表示されているはず。 aboutって入力するとsbtやScalaのバージョンが表示されます。

プロジェクト定義

プロジェクト定義ファイルであるbuild.sbtを作る。 このファイルを作るときはエディタで作ってもいいし、別コンソールからviで作ってもいい。このあともちょくちょくsbtでコマンドを実行するので、sbtプロンプトは残しておいた方が便利だと思う。 ws/sample/build.sbt
name := "sample"

version := "1.0.0"

scalaVersion := "2.9.1"

organization := "teaplanet"

// for Specs2
resolvers ++= Seq("snapshots" at "http://scala-tools.org/repo-snapshots",
"releases" at "http://scala-tools.org/repo-releases",
"releases" at "http://repo1.maven.org/maven2/")

libraryDependencies ++= {
val appEngineVersion = "1.5.5"
Seq(
"com.google.appengine" % "appengine-api-1.0-sdk" % appEngineVersion,
"com.google.appengine" % "appengine-api-stubs" % appEngineVersion % "test",
"com.google.appengine" % "appengine-api-labs" % appEngineVersion % "test",
"com.google.appengine" % "appengine-testing" % appEngineVersion % "test",
"com.google.appengine" % "appengine-tools-sdk" % appEngineVersion % "test",
"org.specs2" %% "specs2" % "1.6.1" % "test",
"org.specs2" %% "specs2-scalaz-core" % "6.0.1" % "test",
"junit" % "junit" % "4.9" % "test"
)
}

build.sbtの空行は大切。

このファイルを作成したら、sbtプロンプトへ戻る。

reload
update

reloadコマンドで更新したファイルを読み込み、updateコマンドで設定を反映する。 初回は依存ライブラリをダウンロードするから時間がかかると思うので、しばし待つ。 ここまででAppEngineの開発が出来る状態になってる。

sbteclipseプラグインを導入する

Eclipseのプロジェクトファイル(.project)を作るためにsbteclipseプラグインを導入する。 ws/sample/project/plugins/build.sbt
resolvers += Classpaths.typesafeResolver

addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse" % "1.4.0-RC4")

pluginsディレクトリは無いと思うので作成する。 ここでもbuild.sbtの空行は意味があるので注意。

またsbtプロンプトへ戻ってお約束のコマンド。

reload
update

これでsbteclipseの導入が完了。

ecl[TAB]

と打って "eclipse" と補完されるようであれば正常に導入されてる。

.projectを作るために次のコマンドを実行する。

eclipse create-src

EclipseでScalaを使う準備

■ Scala IDE for Eclipsehttp://www.scala-ide.org/EclipseでScalaを使うためにScala IDE for Eclipseをインストールしておくこと。

Eclipseの設定でbuildmanagerとしてsbtを使う設定ができる。

でも、これが役に立っているかはわかっていない…。

■ Using the Google Plugin for Eclipse - Google App Engine - Google Code

http://code.google.com/intl/en/appengine/docs/java/tools/eclipse.html#Installing_the_Google_Plugin_for_Eclipseライブラリの依存関係はsbtで解決してるので、こっちは必要ないかも。入れてあるけど。

Eclipseをエディタとして使う

sbtで.projectを作成したので、それを読み込む。File -> Import...Existing Projects into Workspace"Browse..." からws/sampleディレクトリを選択。結果的にこんな感じにAppEngineのライブラリが読み込まれた状態でプロジェクトが出来る。

ルーティン

依存ライブラリが増えた場合はbuild.sbtを修正し、reload -> updateを行う。 その後、eclipse create-srcを実行し.projectを作成。 Eclipseに反映するために、更新(F5)を行う。

この環境はsbtとEclipseの共存なので、好きな方を使えばいい。 テストの実行をリアルタイムに行いたいなら、sbtで "~test" しておけばいいし、トレース実行したいならいつものようにEclipseを使えばいい。

課題

ひとつだけ問題があって、Eclipseでテストは実行できるんだけど、sbtでコンパイルすると次のエラーが発生してテストに失敗する。
NullPointerException: No API environment is registered for this thread. (DatastoreApiHelper.java:118)
これはAppEngine絡みの問題なので、AppEngineを使わない人は出会わないだろうし、設定次第で解決できそうな気もする。

今回作ったプロジェクトはEclipseのプラグインを導入してない。導入した所で.projectファイル作成時に設定が上書きされ、消えてしまう。 ■ eed3si9n/sbt-appengine - GitHub

https://github.com/eed3si9n/sbt-appenginesbt-appengineプラグインもあるんだけど、うまく動かなかったので放置してる。

実は…

このやり方、pomuさんに教えてもらったんだけどね。 [blackbirdpie id="117144635427663872"]

· 2 min read

ScalaからApp EngineのDatastoreに書き込みたいんだけど、使いやすい文法はないものか? ここに直接書いてるから、文法は間違ってるかも?雰囲気だけ伝わればよし。

DSL風?

Entity.store("kind") {
p("name", "Taro"),
p("age", 10)
}

DSL風は嫌いではないけど、プロパティ名を毎回指定するのはどうなんだろ?と思う。KVSってそういうものだけど、もうちょっと楽したいよね?って意味で。

Java風?

class Entity {
val name:Option[Property[String]] = "name".p
val age = "age".p // 型推論でOption[Property[Int]]
val birthday = "date".p.index
}
val e = new Entity()
e.name = "Taro"
e.age = 10 // def age_=(value) = valueとかしておく必要ありか
e.birthday = Date(2001, 7, 15)
Datastore.put(e)

なんとなく、これにはしたくない。せっかくのScalaなんだし…という、感情的理由(^^;

trait利用

trait Person {
// val kind:String = "person" // ここに持たせる?
val name:Option[Property[String]]
val age:Option[Property[Int]]
}

val e = new Entity with Person
e.name = "Taro" // Property[String].valueに"Taro"を設定する or varにする
e.age = 10
Datastore.store("kind", e)

定義(trait)と実装(with)で分かれてるし、これはこれでありかも?と思ってる。 with使ったときのオーバーヘッドってどうなるんだろ?ってことを調べてない。

最近はこんなことも考えてる。

· 9 min read

今日(7/6 水)は勉強会で発表してきました。 資料は社名や人名が含まれてるところがあるので、調整後に後日公開するかも? 参加してくださった方には別途お送りする予定。

もくじ

話した内容はこんな感じ。
  1. Google App Engine概要(の概要)
  2. Scalaの紹介
  3. Google App Engine + Scalaで作ったTwitter bot
  4. ジオサービス
  5. apps script紹介

きっかけ

勉強会で話すきっかけになったのは職場で一緒に仕事をしている人に、 「勉強会で話してみない?」 と言われたこと。 いい機会だし、話してみるか…ということで話すことにしました。 この勉強会で聞いてくださったのは他社の方々。会社間の契約はあるし、数名は面識があるので、知らないわけじゃないけど、聞いてくださる方々は面識ないし、そもそも自社ではないのです。

初めは「20人くらいだし、話す内容はなんでもいいよ」と気楽に言われたので、気楽に考えてました。 「他の人は何を話すんだろ〜?」くらい気楽に。

少しずつ話が具体的になるに連れ、勉強会に近づくに連れ、すこ〜しずつ話が大きくなり、30人ほど出席するという反響ぶり!(人が増えたらビビるやん) 告知メールでは "講師" と書いてあるし、社内Ustreamするとか…広がりすぎですっ!(機器の都合でUst配信無くなったけど) 講師っていうか、情報共有しましょうよ〜みたいな感じにしか捉えてなかったのに、話す人俺だけ。

さらに、追い打ちをかけるかの如く、社内から3人ほど聞きにくると…Σ(゚Д゚) 社長来るし、Google API Expert来るし、賢い海藻猫来るし、もう大変。 Expertいる前でApp Engineの話をするってどうなのよ!? …と思っていたけど、実際、当日になったら、あまり顔を合わせないといえども自社の人だし、心強くも感じられるものでしたε-(´∀`*)ホッ

資料作成

今回はPreziを使って発表したんだけど、資料作成には結構時間かかりました。 まずはiPad2に思っていることを描き出し、それをマインドマップにまとめ、最終的にPreziに落としこむという手順。 iPad2に書いた時点で40ページ超えてて、それをマインドマップに書いたら結構広がった。 さらにPreziにしたらパスの数は130くらいに行きそうな勢いだったので、急遽方法を変え、Scalaの説明はソースコードのみで行いました。 これでも、パスの数は129…って、130くらいになりそうなのを抑えてもまだまだ多かったか...orz

発表

発表自体は、個人的には最低ラインは確保できたかな…と思ってる。 最低ラインですみません…なんだけど、自分としてはなんとか最低ラインまで持って行けて一安心…的な感じ。

事前に2回ほど通しで話してみたけど、ちょっとこれでは質がいいとは言えないな…と思っていたし、自分でも納得出来ていなかった。 今日は自分なりに気になっていたことを気を付けていたこともあって、気になっていた部分はだいぶ減ったと思ってる。 時間配分は前半少し取り過ぎた。聞いてくれる方がいるからには、伝えるところは伝えておきたいな…と思って、追加でちょっと話をした部分が追加の時間だろう。 そんなわけで、後半はちょっと急ぎ気味に進め、ほぼ1時間。 時間的にはよかったけど、時間的にも精神的にも、もう少し落ち着いて話をしたいところだ。 それでも、事前に話した時よりは落ち着いて話せたと思っている。

内容は目次に書いた項目を1時間くらいで話す予定だったため、どうしても説明が薄くなりがち。 今回のターゲットはApp EngineもScalaもほとんど知らない人だろうという想定だったから、あまり深いところは話さず、それらを組み合わせたbotの話や、ジオサービスの話を取り入れ、少しは楽しんでもらえたら…と思っていたんだけど、どうだったんだろう?

内容の薄さと話をカバーすべく、Preziを使ってよかったのかも?これについては少しは楽しめてもらえたと思う。 最後に話したapps scriptは興味がないのか、馴染みがないのか、懇親会でも話が出なかったなぁ。 App Engineよりも、Scalaよりも、Apps Scriptが一番お手軽なんだけど、そんな話をしなかったからか(^^;

まとめ

内容はともかく、練習していた時よりも本番の方が安定して話せたということがよかった。練習の方がよかったら悔やまれる。

仮に、発表がうまくいかなかったとしても、やらないよりやった方がいい経験になるし、自分が楽しまないと見ている人も楽しくないだろうし…ということで、ネガティブなことは考えず、一緒に楽しむことを考えていたのが一番大きいかも。

そんなわけで、自分本位に考えていたところもあり、お聞き苦しいところも多々あったと思いますが、ありがとうございました!

解放

6月はほぼ資料作成ばかりやってたから、やっと解放された感じ(笑) インラインのフレームを交換したいし、スラックラインを注文したいし、サイト構成などなど、色々と考えねば…。 少しずつ色々と進める予定。

· 6 min read

先日、Google Apps ScriptからGoogle App Engineのサービスを利用する際に管理者の判断を行いたかったのでそれについて調べていた。 自分ではある程度解決したと思っている。

発端

そんなとき、こんなmentionが飛んできた。

来月辺り、@vvakameにふやかされる可能性があるので、今のうちに親切に恩を売っておく目的でこれ書いてる。

今のところこうなのかな?と思っている

以下の確認はスクリプトエディタから実行したので、トリガーなどからの実行は未確認です。 テスト中なので一部は "???" 表記にしてます。

まずはOAuthによる認証について。

  • ???.appspot.comではOAuthによる認証が出来た
  • ???.mydomain.xyzではOAuthによる認証が出来なかった

続いて、OAuthが通った場合のユーザ情報について。

  • AppEngine側ではOAuthServiceFactoryからユーザ情報が取得できた
  • AppEngine側ではUserServiceFactoryからユーザ情報が取得できなかった
  • UserServiceFactoryからUser情報が取得できなかったので、管理者かどうか判断できない

所感

mydomain.xyzで認証が通らないのはそのドメインの信頼性を確保できないからということなのだろうか? だとしたら、証明書を登録することによってmydomain.xyzでもOAuthによる認証が通るのかな?と思いつつ、試してない。時間があったらやってみたいところだけど、優先度が下がってる。 mydomain.xyzのToken(key, secret)使ってても、appspot.comでの認証が通ってる。mydomain.xyzはDNSでgoogleの方を向いているから結局は同じということなのだろうか?これはなんとなくありのような、無しのような、微妙な感覚。

ソースコード(apps script)

以下がapps scriptのソースコード。
function initApps_() {
ScriptProperties.setProperty("http", "https://");
ScriptProperties.setProperty("appid", "?????");
ScriptProperties.setProperty("d", "appspot.com");
ScriptProperties.setProperty("consumerKey", "?????.appspot.com");
ScriptProperties.setProperty("consumerSecret", "appssecret");
}

function initMydomain_() {
ScriptProperties.setProperty("http", "https://");
ScriptProperties.setProperty("appid", "?????");
ScriptProperties.setProperty("d", "mydomain.xyz");
ScriptProperties.setProperty("consumerKey", "?????.mydomain.xyz");
ScriptProperties.setProperty("sonsumerSecret", "mydomainsecret");
}

function initOAuthUrl_() {
var http = ScriptProperties.getProperty("http");
var appid = ScriptProperties.getProperty("appid");
var domain = ScriptProperties.getProperty("d");

var ah = http + appid + "." + domain + "/_ah/";
ScriptProperties.setProperty("accessTokenUrl", ah + "OAuthGetAccessToken");
ScriptProperties.setProperty("requestTokenUrl", ah + "OAuthGetRequestToken");
ScriptProperties.setProperty("authUrl", ah + "OAuthAuthorizeToken");
}

// appspotへOAuth --> oauth: ken@mydomain.xyz / user: unknown
function oAuthAppspot() {
initApps_();
var url = "http://?????.appspot.com/oauth/auth";
oAuthTest_(url);
}

// mydomainへOAuth --> Unknown(OAuthRequestExceptionが発生している)
function oAuthmydomain.xyz() {
initMydomain_();
var url = "http://?????.mydomain.xyz/oauth/auth";
oAuthTest_(url);
}

// appspotへOAuth(mydomainのtoken) --> oauth: ken@mydomain.xyz / user: unknown
function oAuthAppspotMyKey() {
initMydomain_();
var url = "http://?????.appspot.com/oauth/auth";
oAuthTest_(url);
}

// mydomainへOAuth(appspotのtoken) --> Unknown(OAuthRequestExceptionが発生している)
function oAuthmydomain.xyzAppspotKey() {
initApps_();
var url = "http://?????.mydomain.xyz/oauth/auth";
oAuthTest_(url);
}

function oAuthTest_(url) {
url += "?" + Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd HH:mm:ss");
Logger.log(url);
try {
var response = UrlFetchApp.fetch(url, option_());
Browser.msgBox(response.getContentText());
} catch(e) {
var msg = "";
for (var i in e) {
msg += e[i] + "n";
}
Browser.msgBox(msg);
}
}

function option_() {
initOAuthUrl_();
var oAuthServiceName = "apps";
var accessTokenUrl = ScriptProperties.getProperty("accessTokenUrl");
var requestTokenUrl = ScriptProperties.getProperty("requestTokenUrl");
var authUrl = ScriptProperties.getProperty("authUrl");
var key = ScriptProperties.getProperty("consumerKey");
var secret = ScriptProperties.getProperty("consumerSecret");

var oAuthConfig = UrlFetchApp.addOAuthService(oAuthServiceName);
oAuthConfig.setAccessTokenUrl(accessTokenUrl);
oAuthConfig.setRequestTokenUrl(requestTokenUrl);
oAuthConfig.setAuthorizationUrl(authUrl);
oAuthConfig.setConsumerKey(key);
oAuthConfig.setConsumerSecret(secret);

var options = {
"oAuthServiceName" : oAuthServiceName,
"oAuthUseToken" : "always"
};

return options;
}

oAuthTest_メソッドでURLに時刻を付けてるのは、キャッシュを返されるような動作があったので、それを回避するため。

スクリプトでひとつ疑問。 ScriptPropertiesで "domain" をキーに指定した場合、getPropertyするとnullになる。これはうちだけ?

ソースコード(App Engine)

以下がApp Engineのソースコード。 言語はJavaじゃなくて、Scala使ってるし、オレオレフレームワーク使ってる。
class OauthController(request:Request, response:Response) extends Controller(request, response) {

def auth = {
try {
val oauth = OAuthServiceFactory.getOAuthService();
val oauthUser = oauth.getCurrentUser match {
case user if user != null => user.getNickname;
case _ => "unknown"
}
val user = UserServiceFactory.getUserService.getCurrentUser match {
case user if user != null => user.getNickname
case _ => "unknown"
}
val msg = "oauth: %s / user: %s".format(oauthUser, user)
response.getWriter.write(msg)
} catch {
case e:OAuthRequestException =>
response.getWriter.write(e.getMessage)
}
}
}

まとめ

今のところ、管理者かどうか判断は出来ていないけど、ユーザは判断できているので、それで凌ごうと思っている。必ずしも開発者(管理者)がアプリの管理者とは限らないので、これはこれでいいや…と思ってる。 お仕事じゃないので、うるさく言う人いないし(笑)

· 4 min read

いつの間にかsbtのバージョンが0.9.1とかなっていたので、慌てて持ってきました! 昨日、sbtバージョンアップしないなぁ…とか思ってたら、ここにあった。 ■ harrah/xsbt - GitHub

https://github.com/harrah/xsbt

sbtはbrewでインストールしてたからbrew管理したかったけど、今の状況を見るとbrew管理しない方がよさそう。

ダウンロード

まずは、ファイルをダウンロードして、展開。 harrah-xsbt-v0.9.1-0-g3ce0e5d.zipというファイル名でした。
unzip harrah-xsbt-v0.9.1-0-g3ce0e5d.zip

ビルド…の前の余談

ビルド時にScala 2.9.0-SNAPSHOTの取得に時間がかかっていて、反応がなかったから2.9.0は消した。
cd harrah-xsbt-3ce0e5d vi project/build/XSbt.scala
lazy val precompiled29 = precompiledSub("2.9.0-SNAPSHOT")
この一行をコメントアウトする。でも、今はこんなことしなくても大丈夫っぽい。

それと、compileをやったらPermGenがOutOfMemoryになった。 そんなわけで、sbtの実行スクリプトは下記にように修正。

java -Xmx1024M -XX:MaxPermSize=512m -jar /usr/local/Cellar/sbt/0.7.4/libexec/sbt-launch-0.7.4.jar "$@"

-Xmx1024M -XX:MaxPermSize=512mを追加して、潤沢にしました。

実際はこれだけあれば足りるらしい。 以下のビルドもこの助言のとおりにやっただけだったりする🙄

ビルド

本題のビルド。
sbt set build.scala.versions 2.8.1 reload update proguard publish-local
updateでエラーが出たので、懲りずに何回もやったらなんとか通った。応答遅かったりと、ネットワークの問題かな? proguardも何回かやったけど、エラーは消えなかった。これは難読化とか最適化とか、本体に直接関係ないのでエラー出たままpublish-localした。

jarを差し替える

targetディレクトリにsbt-launch-0.9.1.jarが出来ているので、これを既存のsbtと差し替えて完了。

注意

0.7.4のときと比べて、かなりコマンドが増えてる!? まず、currentコマンドがなくなってて、自分が何者かわからない。 どっかのディレクトリでsbtを実行すると、いきなり "project" と "target" ディレクトリが出来るので注意。

· 5 min read
前回の投稿でIDEAとsbt-appengineが連携してくれたのは嬉しいけど、ソースを修正するたびにappserverの再起動は面倒!! 出来ることなら、IDEAでコーディング→ブラウザ確認→コーディング→...と繰り返したい。

こちらも実現できたので、方法を紹介。 やったことはこれだけ。

  • JRebelでクラスリローディング
  • クラスファイルの出力先をwebapp配下にする
  • sbtでソースコード監視

環境を簡単に書いておくと、IDEA+sbt(appengine plugin)という組み合わせ。 MyProjectがあって、その下にSubProject1, SubProject2がある構成。このMyProjectとかSubProjectはsbtの管理下にある状態。

まずはJRebelの導入から。 JRebel自体は有償だけど、Scala開発者にはライセンスを提供してくれてるのでこれを活用。 ■ ZeroTurnaround

http://sales.zeroturnaround.com/jrebel.jarを適当な場所に置く。ライセンスファイルもjarと同じところに置いておく。

JRebelでクラスリローディング

クラスリローディング対象とするディレクトリを指定するために、rebel.xmlを作成する。 MyProject/src/test/resources/rebel.xml
<?xml version="1.0"?>
<application>
<classpath>
<dir name="/Users/ken/workspace/MyProject/SubProject1/target/scala_2.8.1/classes" />
<dir name="/Users/ken/workspace/MyProject/SubProject2/target/scala_2.8.1/classes" />
</classpath>
</application>

続いて、sbtでJRebelを有効にする。 sbt-appengine-pluginではJREBEL_JAR_PATHを指定するだけでJRebelが有効になるはずなんだけど、IDEAのsbtコンソールでは環境変数を見てくれないらしいので、直接指定します。 MyProject/project/build/Project.scala

override def scanDirectories = Nil
override val devAppserverJvmOptions = List("-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=2011,suspend=y") ++ List("-noverify", "-javaagent:/Users/ken/dev/jrebel/jrebel.jar") ++ super.devAppserverJvmOptions

JRebelに関係するのは

List("-noverify", "-javaagent:/Users/ken/dev/jrebel/jrebel.jar")

の部分。 ちなみに、XdebugとかはIDEAで接続するための記述。

クラスファイルの出力先をwebapp配下にする

JRebelの設定も終わったので、あとはIDEAからsbtを実行すればいいんだけど、sbtの出力先がappengineの出力先(webapp)とは異なっているためsbtの "~ compile" で監視をしても意味が無い。

そこで、SubProjectの出力先をwebapp配下にします。具体的には、MyProject/SubProject1/target/scala_2.8.1/classesを削除して、MyProject/target/scala_2.8.1/webapp/WEB-INF/classesにシンボリックリンクを張る。 手動でシンボリックリンクを張ればいいんだけど、sbt cleanを実行すると再度やり直さなければならないので、symlinkアクションを作った。 MyProject/project/build/Project.scala

import java.lang.Runtime
lazy val symlinkInstance = new Symlink
lazy val symlink = symlinkAction
def symlinkAction = task { args => task { symlinkInstance(args); None } }
class Symlink() extends Runnable {
def run() = {
def exec(command:String) = Runtime.getRuntime.exec(command)
val subs = Set("SubProject1", "SubProject2")
val webapp = outputPath / "webapp" / "WEB-INF" / "classes"

log.info("Creating directory %s".format(webapp))
exec("mkdir -p %s".format(webapp))

subs.foreach { sub =>
log.info("Processing for %s.".format(sub))
val path = "%s/%s".format(sub, mainCompilePath)
val command = "ln -s ../../../%s %s".format(webapp, path)

log.info("Removing directory %s".format(path))
exec("rm -fr %s".format(path))

log.info("Creating symlink %s -> %s".format(path, webapp))
exec(command)
log.info("")
}
None
}

def apply(args:Seq[String]):Option[String] = {
new Thread(this).start()
None
}

}

コード中の val subs = Set("SubProject1", "SubProject2")" を各自の環境に合わせてください。

sbt reload後に "symlink" アクションが追加されているので、実行してパスを確認して下さい。サブプロジェクトのclassesがシンボリックリンクになっているはず。

使い方

  1. sbtで "symlink" を実行。
  2. IDEAでデバッガ実行(=sbtも起動。前回の投稿参照)
  3. sbtで "~ compile" を実行

簡単に仕組みを説明。 symlinkを実行することによって、appserverの管理下にクラスファイルを出力するようになります。単にシンボリックリンクを張るだけなので初回だけ。sbt cleanしたときは再度実行します。 続いて、IDEAでデバッガを起動し、sbtの "~ compile" を実行してソースファイルを監視します。ソースコードに修正があった場合はsbtの監視によってコンパイルされ、クラスファイルが生成されます(生成先はsbt symlinkによってappsever管理下になっている)。 ブラウザからアクセスすると、JRebelによってクラスの再読込が行われ、sbtには "JRebel: Reloading class 'package.path.to.ClassName'." と出力されます。

これで、IDEAからsbtを起動し、ブラウザで確認しつつコードを書いていくことが出来る♪

· 3 min read

IDEAとsbtを連携してデバッグしたいと思っていたところ@pomu0325さんが解決してくれた。 だけど、appengineの起動はsbtコンソールにdev-appserver-startと入力しなくてはならず、面倒。 どうせなら、IDEAのデバッグ時に一緒にappserverも起動して欲しい。 これを解決する方法がわかったので、紹介。

まずはpomu0325さんのところにも記述があるように "Before launch" の "Run SBT Action" に "dev-appserver-start" を指定する。 このままIDEAから実行してもappserverは起動するんだけど、dev-appserver-startを実行直後にIDEAが接続に行くらしく、コネクションが張れずにエラーとなってしまう。 ということは、コネクションを張るタイミングを遅らせてあげればいいはず。

MyProject/project/build/Project.scalaに以下を追記。

override def devAppserverStartTask(args:Seq[String]) = task {
val status = devAppserverInstance(args)
Thread.sleep(5000)
status
}

これによってdev-appserver-startを実行後、5秒(5000ms)スリープする。 この5秒というのは各自のマシンスペックと相談して、適当に変更してください。

あとはsbtでreload後、IDEAでデバッグするだけ。 appserver起動後にIDEAがコネクションを張りに行くはずです。

余談だけど、こんな記述をしておくとちょっと楽できるかも?

lazy val start = devAppserverStartAction
lazy val stop = devAppserverStop

sbtでdev-appserver-startのエイリアスとしてstartを定義してます。同様にstopも。 appserverの起動はIDEAから可能だけど、sbtのappserverを停止するときにstopが役立つかも?

· 5 min read

IDEAとsbtで開発するための手順。 今回はsbtでベースを作り、IDEAで開発環境を構築する方法。

■ An integrated SBT + IDEA Scala Development Setup ← Decodified

http://www.decodified.com/scala/2010/10/12/an-integrated-sbt-and-idea-scala-dev-setup.html参考にしたサイトはこちら。

使ったことはないが、sbtの定義ファイルからIDEAのプロジェクトファイルを作ってくれるsbt pluginもあるらしい。 ■ sbt-idea 0.1.0 - implicit.ly

http://implicit.ly/sbt-idea-010

以下が本題の手順。

前提

プロジェクトとして "Hello"を、サブプロジェクトととして "World" を作成することにする。 (HelloProject…ハロプロ?…まぁ、いっか)

最終的にはIDEAのプロジェクトとして "Hello" を、モジュールとして "World" が出来ることになる。

sbtの準備

sbtのインストールはご自由に。 ■ simple-build-tool - Project Hosting on Google Codehttp://code.google.com/p/simple-build-tool/

MacBookにはhomebrewの環境を作ってあるので、うちはこれでインストール完了。

brew install sbt

実際に利用するjarファイルはここにインストールされる。 /usr/local/Cellar/sbt/0.7.4/libexec/sbt-launch-0.7.4.jar

sbtによるHelloプロジェクト作成

まずはHelloプロジェクトを作成。
$ cd $WORKSPACE $ mkdir hello $ cd hello $ sbt[object Object]
exitでsbtから抜ける。

続いて、Worldモジュールを作成。

$ mkdir world $ cd world/ $ sbt Project does not exist, create new project? (y/N/s) y Name: World Organization: Hello Version [1.0]: Scala version [2.7.7]: 2.8.1 sbt version [0.7.4]:

exitでsbtから抜ける。

これで、sbtを利用したHelloプロジェクトとWorldモジュールの作成を完了。

IDEAプロジェクトの構築

File - New Project...からプロジェクトの作成を開始する。このまま次へ進む。"Name" は気にせず "Project files location" を選択する。選択するとNameも変更されます。 "Create module" のチェックは外しておく。

プロジェクトの作成は完了したので、続いてモジュールの作成に移る。

真ん中のペインの上にある "+" を押し、モジュールを追加する。次へ進む。モジュールを選択し、次へ進む。"Do not create source directory" を選択し、次へ進む。何も選択せず、次へ進む。"Source Folders", "Test Source Folders", "Excluded Folders" を設定。隣の "Paths" タブを選択し "Output path" と "Test output path" を設定。

sbtコンソールを使う

sbtプラグインを入れておく。sbt-launch.jarのパスを設定しておく。下の "SBT Console" を押すとコンソールが出てくるので、緑色の再生ボタンを押す。 これでsbtが起動したら完了。

トラブルシューティング

最後のsbt起動の際に、sbtの設定を完了しておかないとIDEAが固まる現象が発生した。固まってしまったら、Terminalからsbtのプロセスをkillすることで解決する。

実際に活用するのはこれからなので、使いやすさなどは不明。 IntelliJ IDEAとsbtは使いやすいという噂を聞いたことはあるが、実際に設定手順をまとめてるサイトがなかったので、簡単に書いてみた。

· 5 min read

Mac OS X 10.6でlibusbを操作したいなぁ〜と思い、Scalaでコードを書いてみた。 早速実行してみると、やっかいなエラー発生。

Scala(JavaVM)からlibusbを操作するということは、Java→JNI→libusbとなるため、JNIのライブラリを用意する必要がある。 このファイルはlibusbJava.jnilibであり、コンパイルして作る必要がある。 以前、Windowsで似たようなことをやったときはDLLが提供されていたのでここでは悩まなかった。 ■ Browse libusb-win32 Files on SourceForge.net

http://sourceforge.net/projects/libusb-win32/files/Windowsはこんな風に一式提供されているわけですよ〜。

結論

libusbJava.jnilibは拾ってきた

結果から先に言うと、コンパイルが通らなかったため、libusbJava.jnilibは拾ってきた。 ■ entrainer - Revision 392: /trunk/EntrainerEEG/libusb/macosx/10.6https://entrainer.svn.sourceforge.net/svnroot/entrainer/trunk/EntrainerEEG/libusb/macosx/10.6/ここにMac OS X 10.6用があるのでこれを拝借。

実行準備

実行時にはlibusbが適切な位置に存在することと、JNIがjava.library.pathに含まれている必要がある。 まずはlibusbの準備。
sudo port install libusb-legacy cd /usr/local/lib sudo ln -s /opt/local/lib/libusb-legacy/libusb-legacy-0.1.4.dylib ./libusb-0.1.4.dylib sudo ln -s /opt/local/lib/libusb-legacy/libusbpp-legacy-0.1.4.dylib ./libusbpp-0.1.4.dylib

続いて実行時にJavaVMに渡す情報。

-Djava.library.path=/path/to/libusbJavaDir

これを指定しておかないとエラーが出ます。

Exception in thread "main" java.lang.UnsatisfiedLinkError: no usbJava in java.library.path

ここまでで、やっと実行環境が整った。サンプルコードを実行したところ、libusbは呼べてるみたいなので、あとは操作用のコードを書いていくだけ。 環境構築にやたらと手間取った。


出来なかったけど、やったこと

以下は色々と試したけど、結果的にコンパイル出来なかったので、あまり役に立たないかも?

Antでコンパイル

■ Java libusb / libusb-win32 wrapper » Installationhttp://libusbjava.sourceforge.net/wp/?page_id=8ここを参考にコンパイルを試みた。

■ libusbjava - Revision 275: /

https://libusbjava.svn.sourceforge.net/svnroot/libusbjava/ソースコードはこちら。

ソースコードを持ってきて、Antを実行。

ant mac

エラーメッセージが色々と出てきた。

libusb.dylibが無いよ

libusb.dylibとlibusbpp.dylibが無いと言われた。 ここで見た初めての拡張子 "dylib" とは何だろう?と調べてみるとライブラリファイルだった。Linuxでいうsoファイル。 これをインストールするためにMacPortsを利用。
sudo port install libusb
libusbのインストールは完了したものの、libusbppが見当たらない。libusbをコンパイルしてみても出てこない。

■ libusb

http://www.libusb.org/オフィシャルサイトに行ってみるとバージョンが大きく2つに分かれているようだ。
Stable API: libusb-1.0 Legacy API: libusb-0.1

libusbjavaのオフィシャルサイトもよ〜く見てみるとこのような説明が書いてあった。

libusb 0.1: is supported libusb 1.0: has a completely new API is not supported (see the libusb wiki for details)
さっき、MacPortsでインストールしたlibusbは1.0の方であり、libusbjavaがサポートしていないと判明。
sudo port uninstall libusb sudo port install libusb-legacy
libusb 1.0は削除し、libusb 0.1をインストールし直した。

それでもコンパイルエラーが発生するので、LibusbJava.cppのincludeを修正したり、簡単な施策はいくつか試したものの、コンパイル出来ず〜。 そして諦めた。

usbjava2

■ stefanofornari's usbjava2 at 09e0f610bfde0ba287642fb74d339ea9b0bb8c1d - GitHubhttp://github.com/stefanofornari/usbjava2/tree/09e0f610bfde0ba287642fb74d339ea9b0bb8c1dこんなのも見つけたが、これもうまくいかなかった。

· 2 min read

年末にGAE/Jの新機能などなどを期待して、またGAE/Jを使ってみようと思った。 VPS+MongoDBや、VPS+Cassandraという組み合わせと迷ったんだけど、VPSの管理をせず、作ることに専念できそうなのでGAE/Jを選択。 テキストの検索はどうしよう?など、解決しなければならないことはあるけど、先の話だし、あとで考えることにする。

Eclipse 3.6も出て、少し経つし、使ってみることにした…が、Mavenプラグインのインストールでエラー発生。 仕方ないので、断念し、今まで通りEclipse 3.5にした。

EclipseでGAE+Maven+Scalaの環境構築、これってどう手順がいいのだろう? 今回は、GAEプロジェクト作成 → フォルダ構成修正 → Mavenプラグイン適応 → Scalaプラグイン適応という順で行った。 途中でフォルダ構成を変更したのは、src/main/javaの形式にするため。 今のところ、Mavenは依存関係を解決するためにしか使ってないので、これで困ってはいない。

· 5 min read

Twitter絡みで、OAuth Echoを使いたかったので書いてみた。

ネットの情報を色々と見ていると、人それぞれというか、動くのかな?っていうのもあったりして、軽く振り回された感あり :| 書いたソースコードはgithubに置いておきます&下の方に貼り付けておきます。

http://gist.github.com/497821お役に立つようでしたら、ご自由にどうぞ。

以下、RequestTokenを取得するURLの作り方を簡単にまとめます。 RequestTokenはTwitterに署名付きのパラメータを送ることによってもらうことが出来きます。

http://twitter.com/oauth/request_token?&<各種パラメータ>&<署名>

この各種パラメータは次の通り。

  • oauth_consumer_key: 各自のConsumerKey
  • oauth_signature_method: "HMAC-SHA1" 他にもあるらしいが今回はこれ固定。
  • oauth_nonce: その場限りの値。ランダム文字列を利用。
  • oauth_timestamp: 現在時刻を秒で表した値。1900年を起算にっていう例のやつ。
  • oauth_version: OAuthのバージョン値。"1.0"固定。

URL構築時にはお約束の key=value を&で連結。

署名がちょっと面倒。 まずは署名の準備。署名にはHMAC-SHA1形式を利用。 キーを作成するときは "ConsumerSecret&OAuthToken" とする。もちろん、ConsumerSecretもOAuthTokenも自分の値で書き換えてください。 初回はOAuthTokenをまだ取得してないので空文字列となり、結果的に "ConsumerSecret&" となります。

続いて、署名するための文字列を作成します。

  1. パラメータを名称順にソート。
  2. パラメータを連結(key1=value&key2=value2&...)。これを仮に<Params>とする。keyとvalueをそれぞれエンコードする必要があるという情報も見たが、今回はエンコードしても同じなので、してません。
  3. "GET&encode(http://twitter.com/oauth/request_token)&encode(<Params>)" を作成。encodeはURLエンコード。

ここで作成した "GET..." という文字列を先程作成したキーで署名します。 署名した値をBase64エンコードし、その後、URLエンコードします。 これがRequestTokenをもらうためのURLの一部となる署名です。

生成したURLを叩いて "oauth_token=..." というレスポンスが帰ってくれば成功です。

もし、うまくいかなかった場合に見直した方がいいと思われるところ。

  • 署名用のキーを生成するところ。ConsumerSecretとOAuthTokenを利用しているか?OAuthTokenが無い場合は "ConsumerSecret&" のように&で終わっているか?
  • 署名する値("GET&http://...")の生成は正しいか?
  • 署名した値をBase64→URLエンコードしているか?

それほど難しいわけじゃないけど、はまるとミスっている箇所を見つけるのがちょっと大変かも。

· One min read

映画「ソラニン」公式サイト

http://solanin-movie.jp/ソラニンを観てきました。 初めは宮崎あおいが歌うという興味本位だったけど、泣ける場面もいくつかあって、出演者も素朴な感じでよかったと思う。

ソラニンの原作者は何を思ったのか "おやすみプンプン" という、よくわからないマンガを書いている。

それがこのキャラクター。

おまけ。

帰りに買ってきた本。

· One min read

Scala 2.8.0.RC2でspecsを使おうと思ったらエラーが発生してコンパイルさえ通らない!! というわけで調べてみました。

結果的には、2.8用のspecsを使えってことでした。 その2.8用のspecsはどこにあるかというと・・・散乱してます。

■ 2.8

http://code.google.com/p/specs/downloads/list本家サイトにもあるものの、バージョンがちょっと古め?

■ git.jetbrains.org - idea/scala-plugin.git/tree - SDK/lib/

http://git.jetbrains.org/?p=idea/scala-plugin.git;a=tree;f=SDK/lib;hb=HEADJetBrainsの中のスレッド経由で見つけたページ。

これでScala 2.8.0.RC2でもspecsが使えるようになりました。 既にRC3が出ているけど、bugfixなはずだからRC3に移行しても使えるでしょう。

· 6 min read

今日はScala(2.8.0.RC2)の型推論ではまった。 このコードをコンパイルするとエラーが発生する。

import scala.collection.mutable. { Map, ListBuffer }

object TypeError {
val list:List[Option[String]] = List(Some("a"), Some("b"))

var result:ListBuffer[String] = ListBuffer()
var map:Map[String, ListBuffer[String]] = Map()
list.foreach(v =>
v match {
case Some(s) => result += s
case _ => map += ("" -> ListBuffer(""))
})
}

意味不明だったため、Twitterでつぶやいてみたら、何人かの方々が一緒に考えて下さるという、とてもありがたいことがありました。 一緒に考えてくださった方々、ありがとうございました。

以下、Twitter上で教えていただいたことを元に簡単にまとめます。

scalac -Xprint:typer typeerror.scala とコンパイルすると次のようなエラーメッセージが表示される。

[[syntax trees at end of typer]]// Scala source: typeerror.scala
package <empty> {
import scala.collection.mutable.{Map, ListBuffer};
final object TypeError extends java.lang.Object with ScalaObject {
def this(): object TypeError = {
TypeError.super.this();
()
};
private[this] val list: List[Option[String]] = immutable.this.List.apply[Some[java.lang.String]](scala.Some.apply[java.lang.String]("a"), scala.Some.apply[java.lang.String]("b"));
<stable> <accessor> def list: List[Option[String]] = TypeError.this.list;
private[this] var result: scala.collection.mutable.ListBuffer[String] = scala.collection.mutable.ListBuffer.apply[String]();
<accessor> def result: scala.collection.mutable.ListBuffer[String] = TypeError.this.result;
<accessor> def result_=(x$1: scala.collection.mutable.ListBuffer[String]): Unit = TypeError.this.result = x$1;
private[this] var map: scala.collection.mutable.Map[String,scala.collection.mutable.ListBuffer[String]] = scala.collection.mutable.Map.apply[String, scala.collection.mutable.ListBuffer[String]]();
<accessor> def map: scala.collection.mutable.Map[String,scala.collection.mutable.ListBuffer[String]] = TypeError.this.map;
<accessor> def map_=(x$1: scala.collection.mutable.Map[String,scala.collection.mutable.ListBuffer[String]]): Unit = TypeError.this.map = x$1;
TypeError.this.list.foreach[scala.collection.mutable.Iterable[_ >: String with (String, scala.collection.mutable.ListBuffer[String]) <: java.lang.Object] with PartialFunction[Int with String,java.lang.Object] with scala.collection.mutable.Builder[String with (String, scala.collection.mutable.ListBuffer[String]),Iterable[java.lang.Object] with PartialFunction[Int with String,java.lang.Object]{def companion: scala.collection.generic.GenericCompanion[Iterable[Any]{def companion: scala.collection.generic.GenericCompanion[Iterable[Any]]}]}] with scala.collection.generic.Shrinkable[String]{protected def clone(): scala.collection.mutable.Iterable[_ >: String with (String, scala.collection.mutable.ListBuffer[String]) <: java.lang.Object] with PartialFunction[Int with String,java.lang.Object] with scala.collection.mutable.Builder[String with (String, scala.collection.mutable.ListBuffer[String]),Iterable[java.lang.Object] with PartialFunction[Int with String,java.lang.Object]{def companion: scala.collection.generic.GenericCompanion[Iterable[Any]]}] with scala.collection.generic.Shrinkable[String]{protected def clone(): scala.collection.mutable.Iterable[_ >: String with (String, scala.collection.mutable.ListBuffer[String]) <: java.lang.Object] with PartialFunction[Int with String,java.lang.Object] with scala.collection.mutable.Builder[String with (String, scala.collection.mutable.ListBuffer[String]),Iterable[java.lang.Object] with PartialFunction[Int with String,java.lang.Object]{def companion: scala.collection.generic.GenericCompanion[Iterable[Any]]}] with scala.collection.generic.Shrinkable[String]{protected def clone(): scala.collection.mutable.Iterable[_ >: String with (String, scala.collection.mutable.ListBuffer[String]) <: java.lang.Object] with PartialFunction[Int with String,java.lang.Object] with scala.collection.mutable.Builder[String with (String, scala.collection.mutable.ListBuffer[String]),Iterable[java.lang.Object] with PartialFunction[Int with String,java.lang.Object]] with scala.collection.generic.Shrinkable[String]{protected def clone(): scala.collection.mutable.Iterable[_ >: String with (String, scala.collection.mutable.ListBuffer[String]) <: java.lang.Object] with PartialFunction[Int with String,java.lang.Object] with scala.collection.mutable.Builder[String with (String, scala.collection.mutable.ListBuffer[String]),Iterable[Any] with PartialFunction[Nothing,Any]] with scala.collection.generic.Shrinkable[String]{protected def clone(): Iterable[Any] with PartialFunction[Nothing,Any] with scala.collection.mutable.Builder[Nothing,Any] with scala.collection.generic.Shrinkable[String] with Mutable}; def iterator: Iterator[java.lang.Object]}; def iterator: Iterator[java.lang.Object]}; def iterator: Iterator[java.lang.Object]}; def iterator: Iterator[java.lang.Object]}](((v: Option[String]) => v match {
case (x: String)Some[String]((s @ _)) => TypeError.this.result.+=(s)
case _ => TypeError.this.map.+=(scala.this.Predef.any2ArrowAssoc[java.lang.String]("").->[scala.collection.mutable.ListBuffer[java.lang.String]](scala.collection.mutable.ListBuffer.apply[java.lang.String]("")))
}))
}
}

typeerror.scala:8: error: kinds of the type arguments (Iterable[Any]{def companion: scala.collection.generic.GenericCompanion[Iterable[Any]]}) do not conform to the expected kinds of the type parameters (type CC) in class GenericCompanion.
Iterable[Any]{def companion: scala.collection.generic.GenericCompanion[Iterable[Any]]}'s type parameters do not match type CC's expected parameters: <refinement> has no type parameters, but type CC has one
list.foreach(v =>
^
one error found

型を推論しているときに、推論しきれなかったっぽいです。

次のコメントを読んでもらった方がわかりやすい。

対応策としては次のようなことを教えていただきました。

なるほど、foreach[Any](...とすることによって型推論をしなくて済むようにするらしい。 今回の現象は不具合なのか、こういうエラーが発生するケースなのかは不明。 こうやって原因がわかると、値を返す必要がないのにmatchは変かもなぁ〜と、ちょっと思ったり。

ちなみに、foreach内のmatch式を関数にするとエラーは発生しない。

import scala.collection.mutable. { Map, ListBuffer }

object TypeError {
val list:List[Option[String]] = List(Some("a"), Some("b"))

var result:ListBuffer[String] = ListBuffer()
var map:Map[String, ListBuffer[String]] = Map()
list.foreach(v => func(v))

// match部分を関数にした。
def func(v:Option[String]):Unit = {
v match {
case Some(s) => result += s
case _ => map += ("" -> ListBuffer(""))
}
}
}

これはfunc関数の戻り値がUnitということで型推論をする必要がないということらしい。 そもそも、型を返す必要がないんだからこのように関数にしてしまう方がいいのかもしれない。

Scala、奥が深いです・・・。

· 5 min read

ここ数日、HTMLチックの構文解析のコードを書いてる。 やりたいことはHTMLの解析に絡むんだけど、テキストの置換をしたいだけ。 なので、HTMLの仕様に沿っているか?というと、それほど沿ってない。 タグで括られたテキストをいじりたいなぁ〜ってことに対応する程度。 今後も曖昧なHTMLをさらに曖昧に書く人たちはいるだろうという推測も兼ね、必要最低限な解析で幅広く対応。

scala.util.parsing.combinatorを利用しているので、構文解析自体、似非かも?

実装していて思ったのは、(わかっていたけど)曖昧性が強いなぁ〜ってこと。 XMLならもっと簡単なのに・・・ってのが多い。 例えば、こんな感じ。

  • 閉じタグの省略。
  • 属性を " " で括る必要が無い。
  • 属性名しか存在しない。
  • scriptタグ内に "<" が存在する。

閉じタグを省略した場合、ルールに則って推測するのだが、これ、省略〜。 閉じタグ省略時は子要素としては扱いません。 インライン要素の場合は子要素にするとか、その程度の判断くらいは可能だけど、今回は "マークアップされたテキストの解析" を目的としているため、タグの名前は持ってない。 そのため、どれがインライン要素か?って情報は持ってないし、持ちたくない。 これで困ることがあるとしたら、その要素が置換対象となっていた場合。 それも、きちんとタグを閉じればいいわけだし、これくらいの制約はいっか、と判断。

HTMLに限らずだと思うけど、構文解析する場合、解析した結果、何かをしたいはず。 となると、解析前の情報はいらないことが多々あって、ホワイトスペース(改行とか空白とか)の情報って人が見やすくするためだけにあったりするのだが、今回は解析前の状態に戻したかったりもする。 解析前の状態に戻すということはホワイトスペースも必要になるので、この情報もきっちり取得してます。

字句解析に正規表現を使ってるので、処理速度が遅かったら本格的に書きなおさなきゃならないんだけど、どうなることやら。 今のところ、解析結果を保持するつもりだけど、保持の方法はまだ考えてない。 安易なのは、まんまオブジェクト(笑)

悩んだことがひとつ。 改行を含む文字列をなんでも取得したかったんだけど "." では取れなかった。 DOT ALLオプションも見当たらないし・・・と思って見つけたのが "[sS]" という表記。 "スペースとスペースじゃないもの" って、全部ってことじゃん!ってことで、解決。 相反するものの和を取ったら全部です(笑)

· 2 min read

今日、Scalaって強力だなぁ〜と思ったことを2つ。

まず、コレクションの扱い。 JavaだとIteratorにして、nextで・・・みたいな実装をすることがあるが、Scalaだとこれがめんどい。 foreach使いたいのに・・・というときはこの一行を!

import scala.collection.JavaConversions._

これで、 List<String> だとしても、foreachメソッドが追加されます。 (Scalaのバージョンが2.8の場合)

参考URL ■ Twitter / Kota Mizushima: @itoasuka import ....JavaC ...

http://twitter.com/kmizu/status/12477994337
import ....JavaConversions._ すれば、あとは、scalaの*mutableな*SetからJavaのSetへの自動変換が提供されます。

もうひとつ、強力だと思ったのはキャスト。 Javaではこう書く。

((Text)text).getValue();

Scalaだとこう書く。

text.asInstanceOf[Text].getValue

直感的〜!!ということです。

キャスト慣れしてる場合はなにそれ?って感じかもしれないけど、左から右に、思い通りに書けるのがステキ。

· One min read

昨日の記事のScalaのブロックを省略したい件、解決! Twitterでつぶやいたら、ヒントだけじゃなくてわざわざコードまで書いてくれた。

def hoge(n:String)(implicit block:String => Unit=null):Option[String] = {
println(n)
if (block != null) block(n)
return Some(n)
}

val n = hoge("2")
val m = hoge("2") { s=>
println(s + ":" + s)
}

昨日もimplicitを使ったけど、出来なかったのは、どっか間違ってたのかな? implicitの理解も浅いからまた調べ直すか。

これで思い通りに書けそう♪

· One min read

Scalaの構文で悩み中。 ブロックを引数に持つ関数を、ブロック無しでも使うことは出来ないのだろうか? (イメージ的にはrubyのNet::HTTP#getに近いかな)

例えば、こんな感じ。

def hoge(n:String)(block:String => Unit=null):Option[String] = {
println(n)
if (block != null) block(n)
return Some(n)
}

val n = hoge("2")
val m = hoge("2") { s=>
println(s + ":" + s)
}

実際にはこういうことは出来ないらしい。

現実的にはこんな感じ?

def hoge(n:String, block:String => Unit=null):Option[String] = {
println(n)
if (block != null) block(n)
return Some(n)
}

val n = hoge("2")
val m = hoge("2", {s=>
println(s + ":" + s)
}
)

· One min read

■Using the Scala IDE for Eclipse with JUnit | The Scala Programming Language

http://www.scala-lang.org/node/363ここに載ってる例だとエラーが発生すると思う。

簡単な例を載せておく。

package example.junit

import org.junit._
import org.junit.Assert._

class TestJUnit {
@Test
def greeting():Unit = {
val hello = new Hello()
assertEquals("Hello!", hello.greeting)
}
}

TestNGってのもあるし、そっちに移行しようかな? 今のところ、たいしたテストコード書いてるわけじゃないから、どっちでも問題ないんだけど。

· 3 min read

やっと本のリストを作成完了。 もう、読まない本もあるので、そういうのは差し上げます。 (本のリストを見たかったら連絡ください) 雑誌は専門書が多いし、まだ整理してないので今回は載ってません。

最近はあまり本を読んでなかったのと、ゲーム(DS)をしてなかったこともあって、あまり魅力的なリストではないかも。 どちらかというと、息抜きに読んでたマンガが溜まってた。 なので、今回は不要なDSゲーム無し! 買ったまま封さえ開けてないレイトンが残ってるくらいだから(^^;

以下、本には直接関係ない話。 リスト化するのにScala使ったんだけど、言語のコンセプトが好きかも。 データを扱うのに適したAPIが揃ってて、さらに関数型言語だからシンプルに表現出来る感じ。 例えば、あるデータの固まりに対して、

  • 同一の処理を行える
  • 条件で抽出出来る

というふたつがとっても強力。 本のリスト化にあたっても "著者" という共通の情報だけを抽出したり、抽出したデータを出力するために共通のメソッドを呼んだり、頭の中で構成した手順をそのまま適応出来る。 気付くのにちょっと時間がかかった部分もあるが、気付いてしまえばかなり応用が効きそう。

· 2 min read

Scalaのテストケースが保存出来ない!!と思っていたが、コードの書き方が悪かったらしく、プラグイン内部でエラーが発生してその絡みで保存出来なかったみたい。 あやしい部分を書き直したら解決。 プラグインがまだ安定してないから仕方ないんだけど、原因が発覚するまで時間かかった〜。 こういうこともあるってことさえわかれば、あとは出会わないように気を付ければいいだけだし、なんとかなりそう。

Scalaとはなんの関係も無いが、こんなディストリビューションがいつの間にか出てた!! ■funtoo -- Daniel Robbins, gentoo, linux, keychain, metro, articles

http://www.funtoo.org/すっごく興味はあるものの、このソースコードコンパイル方式は時間がかかるんだよなぁ〜。

· 3 min read

AmazonのWebServiceを利用して、ISBNから商品情報を整理してみた。 まだ著者情報を整理出来て無いから公開しないけど、近々公開予定。

今回はScalaを使ってAmazonのAPIを叩いた。 現時点でソースコードを眺めてみると、すっごくすっきり&シンプル。 これだけ?ってくらい少なくて、汎用性もある気がする。

そう思った理由はScalaで書いたからだろう。 なぜScalaで書くことによって、そう思ったのか? これはどうってことなく、

  • 記述量が少ない
  • 関数型言語

の特徴のおかげだと思う。 この特徴ってScalaに限ったことではないので、LLなら似たような感想を持ったと思う。

関数の中に関数を定義出来るし、再帰も見易いし、いい感じ♪

今回はなるべくループ(for, while)を使わないように気をつけて書いてみた。 forを使うと "ここからここまで" みたいな指定になってしまうし、whileを使うと "この条件を満たす間" みたいになってしまうため。 それよりもデータに対して "こう処理したい" ということだけを指示してシンプルにしたかった。 そのために、再帰関数で処理をさせたり、foreachを使ってあるデータ(集合)全体に処理をさせたりした。

再帰関数を書いていると "あっ、ここ、並列処理になるかも?" みたいなことを目の当たりにする。 実際、並列処理になるかはコンパイラやインタプリタしだいというところもあるが、並列処理になる可能性は十分にある。

· One min read

Base64で軽くはまった。 ■Amazon アソシエイト(アフィリエイト) - ヘルプ

https://affiliate.amazon.co.jp/gp/associates/help/t126/a11
Base64 encoder = new Base64(); signature = new String(encoder.encode(rawHmac));
インスタンス化してencodeメソッドを呼んでます。 ただ、これだと動作せず。 これをこう書き直すと動く。
signature = new String(Base64.encodeBase64(rawHmac));
このサンプル通りに書いたわけじゃないから、このサンプルをそのまま使う分には問題ないのかもしれないけど、もし、動かないことがあったらお試しあれ。

このメソッドの違いは76文字制限をチェックするかしないからしい。 前者だとエンコードした文字列の末尾にCRLFが付く。 もしかしたら、この辺がうまくなかったのかもしれない。

· 2 min read

MacPortsに慣れないなぁ〜。 それほど使ってないからなんだけど、CUIとGUIが混在してるせいもありそう。 WindowsならインストールはGUIのインストーラを使ってやることが多いし、Linuxならほとんどコマンドでインストールしてしまうけど、Macは両方。 普段使うのはユーティリティだったりするからGUIのインストーラで、開発に関係するライブラリなどはMacPorts。

Scala、どうやってインストールしたんだっけ?と思ったら、これもPortsでした。 Scalaのバージョン2.8が出てきたこともあって、パッケージ名にバージョンが入ってた。

sudo port install scala28

パスが通ってなかったので/opt/local/share/scala-2.8のファイルのいくつかを/usr/local/binにシンボリックリンクした。

$ scala -version Scala code runner version 2.8.0.Beta1-prerelease -- Copyright 2002-2010, LAMP/EPFL
こんな感じになり、使えるようになります。

· One min read

FizzBuzzをscalaで書いてみた。 かっこいいコードではないが、書けた。 それよりも、Scalaの文法の方が怪しいよ・・・実際にある程度書かないと覚えないな。

書きたいことが違った。 Eclipseで書いたんだけど、Command + F5で実行しようとしたら、Macの読み上げ機能が立ち上がって、 「1, 2, Fizz, 4, Buzz, Fizz, ...」 と読み上げられたよ!!(笑) 「わん, つー, ふぃず, ふぉー, ばず, ふぃず, ...」って(笑)

■Fizz Buzz - Wikipedia

http://ja.wikipedia.org/wiki/Fizz_Buzz

■どうしてプログラマに・・・プログラムが書けないのか?

http://www.aoky.net/articles/jeff_atwood/why_cant_programmers_program.htm

· One min read

本を4冊買ってきた。 今日はちょっと高め。

桜井亜美の小説。これも同じく桜井亜美。 こっちは薄いから比較的早く読み終わるかな。これはscalaの本。 じっくり読みたいけど、時間がなさそう。 内容は面白そうなので期待😄 この本は高かった。これは既に読み終わった。

この他にも2冊気になったのがあったんだけど、幸い(?)売ってなかった。 じっくりと本を読む時間が欲しい。

· 2 min read

scala.annotaitonなんてのがある。 scala.annotaiton.Annotationを使えば定義できるっぽいが使い方がわからん😔

わからないのがローカル変数の取得方法。 スクリプト言語ってこれが出来る/出来ないでかな~り違うと思う。

scalaってJavaの考え方がだいぶ簡略化されていて使いやすいと思う。 ・・・が、反面Javaのちょっとお堅いというか、めんどくさい部分が消え、スクリプト言語化されてる。 まったくもってその通りだと思ったから使いやすいのだろうけど、Java知らずの人だと「なぜそうなるの?」的なところがわからないと思う。 うまく隠蔽したといえばその通り😄

視点しだいかな。

· 2 min read
昨日のjava.lang.NoClassDefFoundErrorの件だけど、最終的にはlog4jのクラスがなかったことに起因してた。 Eclipse上では動く(動いたように見えた?)ときと動かないときが曖昧だし、Problemとして報告してくれないし、わからん!

どうやって原因を見つけたかというと、scalacコマンドでひとつひとつコンパイルして追っていった。 まだまだPluginは信用できないな・・・と思った。

実際に出ていたエラーは「オブジェクトがないよ!」って言われていたので、オブジェクトも疑うべきだったのかもしれないが、そのオブジェクトではエラーが全く無いように見えるんだよね。 とりあえずこの件は解決。

· 2 min read

scalaをちょっといじってみた。 下記のようなエラーが発生。

java.lang.NoClassDefFoundError: sdb/SDBTest Caused by: java.lang.ClassNotFoundException: sdb.SDBTest
もんのすごい基本的なエラーだが、理由がわからない。 問題なく動作するときと、しないときがあるから。 (動作するときとしないときの現象の切り分けは出来つつある。下記参照)

· One min read

The Scala Programming Language

http://www.scala-lang.org/index.htmlたまたまこんな言語を発見! JavaVM上で動作!! だ〜いぶ前から存在はしていたらしい。