2009年 07月 12日

hookhub というのをつくりました

http://www.hookhub.com/

  1. Web Hook に対応したサイトからのフックを受けて、
  2. それをいい感じに JS で加工し、
  3. 他のサービスにリクエストを投げたり、自分にメールを送ったりすることができます。

例えば、

  • はてブしたエントリを twitter に
  • github に push したら twitter に
  • Google Code にコミットしたら twitter に

とか

  • はてブで [あとで読む] タグをつけたら、Remember the milk にタスクをつくる
  • はてブのお気に入りユーザが [culture] タグをつけたら即自分にメールを投げるとか

とか、些細なことを自分でホストすることなくできるようになります。

こういったルールは全て JavaScript で書くようになっていますが、Fork と Config 機能で他の人のをコピってすぐ使えたりします。

例えばはてブ → twitterとかだと、

  1. http://www.hookhub.com/cho45/hook/1004 を Fork して Save
  2. Config で twitter_user, twitter_pass を入れる
  3. はてブの「イベント通知を受けとるURL」に Hook URI を入力すればできます


ちなみに GAE + Scala でつくっていて、フック起動時の JS の実行には Rhino を使っています。Rhino では頑張ってサンドボックスを作っていろいろ制限をかけているつもりです。

2009年 07月 11日

gerry++

Android で Hello World するまで

cd ~/project/hello-world-android
android create project --target 3 --path . --activity HelloWorld --package com.example.HelloWorld
ant debug
adb install bin/HelloWorld-debug.apk

次からは

adb install -r bin/HelloWorld-debug.apk


src/com/example/HelloWorld/HelloWorld.java をみると、R.layout.main というのが view に設定されてる。res/layout/main.xml が実体っぽい。適当に書きかえて

adb debug
adb install -r bin/HelloWorld-debug.apk

するととりあえず自動的に起動中のアプリが終了してインストールが走るようになる

ファイルシステム

adb install されたアプリケーションは /data/app に入っている。これは

$ adb shell
# ls /data/app
# exit

とかでわかる

開発

adb shell "am start -a android.intent.action.MAIN -n com.example.HelloWorld/.HelloWorld"

.HelloWorld の部分は AndroidManifest.xml の /manifest/application/activity/@android:name っぽい?

http://gist.github.com/145085 Rakefile

Scala, ant

ant の compile タスクを修正して scalac が走るようにする。

カレントディレクトリの tools 以下に scala-compiler.jar を、libs 以下に android-library.jar をおいて

build.xml の最後に

    <target name="compile" depends="dirs, resource-src, aidl">
        <javac encoding="ascii" target="1.5" debug="true" extdirs=""
            srcdir="gen"
            destdir="bin/classes"
            bootclasspath="${android-jar}">
        </javac>

        <javac encoding="ascii" target="1.5" debug="true" extdirs=""
            srcdir="src"
            destdir="bin/classes"
            bootclasspath="${android-jar}">
        </javac>

        <taskdef resource="scala/tools/ant/antlib.xml" classpath="tools/scala-compiler.jar:libs/android-library.jar" />
        <scalac
            force="changed" deprecation="on"
            srcdir="src" includes="**/*.scala"
            destdir="bin/classes">
            <classpath>
                <pathelement location="${android-jar}"/>
                <fileset dir="libs" includes="*.jar"/>
            </classpath>
        </scalac>
    </target>

を書く、これだと scala-library.jar の変換で死ぬので

$SDKHOME/platforms/android-1.5/tools/dx 

に javaOpts="-Xmx512M" とか書くととりあえず通る。20秒ぐらいかかる

2009年 07月 07日

Ruby っぽいブロックのとりかた

object Main extends Application {
	def rubylike (s:Int, e:Int)(f:Int => Unit) = {
		(s to e).foreach { i =>
			f(i)
		}
	}

	rubylike(1, 10) { i =>
		println(i)
	}
}

部分的にカリー化して最後に関数をひとつだけとるようにすると、いい感じに書ける

2009年 07月 05日

ゆの in Scala

//#! scala

object X {
	var mes = ""
	def / (o: => Any) = this
	def < (m:Symbol)  = { mes = m.toString; this }
	def ! () = { println("ひだまりスケッチ×365 " + mes + "!") }
}

(0 to 10).foreach {
	X / _ / X < '来週も見てくださいね!
}

ふつうにかくとふつうにできてしまうのでおもしろくない。 → これがおもしろい http://d.hatena.ne.jp/kmizushima/20080710/1215654623

don't say sanitize

2009年 07月 04日

Scala で WAF を書きはじめる、前の第一歩

じゃう゛ぁとかよくわからないし的なレベルではじめようとすると前段階で躓くので、Rack とか HTTP::Engine 的に、あとはハンドラを書けばokみたいな状態にするまでのメモ

Java なウェブアプリは WAR とかいう形式にしてやると、サーブレットコンテナ (Jetty とか TomCat とか?) というものがよしなに起動してくれるようになるらしいです。GAE/J もそう。なので、それに沿ったものを作ります。

.
|-- src
|   |-- META-INF
|   |   `-- jdoconfig.xml
|   `-- net
|       `-- lowreal
|           `-- skirts
|               `-- HttpRequestDispatcher.scala
`-- war
    |-- WEB-INF
    |   |-- classes
    |   |   |-- META-INF
    |   |   |   `-- jdoconfig.xml
    |   |   `-- net
    |   |       `-- lowreal
    |   |           `-- skirts
    |   |               `-- HttpRequestDispatcher.class
    |   |-- lib
    |   |   `-- scala-library.jar
    |   |-- logging.properties
    |   `-- web.xml
    |-- css
    |   `-- base.css
    |-- images
    |-- index.html
    |-- js
    `-- static

war ディレクトリ以下が公開される (WEB-INF を除く) ディレクトリで、WEB-INF 以下にアプリケーション実体があり、WEB-INF/web.xml でルーティング設定をします。

web.xml ではふつう、URI -> Servlet の対応を XML で書くっぽいですが、そこらへんのことをむしろ自分でやりたいわけなので

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
  <filter>
    <filter-name>HttpRequestDispatcher</filter-name>
    <filter-class>net.lowreal.skirts.HttpRequestDispatcher</filter-class>
    <init-param>
      <param-name>foo</param-name>
      <param-value>hogehoge</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>HttpRequestDispatcher</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

というふうにフィルタで全部横取りします。

あとはフィルタ書けばいいだけなので

package net.lowreal.skirts

import javax.servlet._
import javax.servlet.http.{HttpServlet, HttpServletResponse, HttpServletRequest}

class HttpRequestDispatcher extends Filter {
  var static = """^/(?:css|js|images|static).*""".r
  val through = """^/_.*""".r
  var router:HttpRouter = null

  def init (filterConfig: FilterConfig) = {
    println("initializing...")

    val config_foo = filterConfig.getInitParameter("foo")

    println("initialized config: " + config_foo)
  }

  def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) = {
    // ここでなんかする
    (request, response) match {
      case (req: HttpServletRequest, res: HttpServletResponse) =>
        val path    = req.getRequestURI
        val statics = static
        path match {
          case statics() =>
            chain.doFilter(request, response)
          case through() =>
            chain.doFilter(request, response)
          case _ =>
            // router.dispatch(req, res)
        }
      case _ => chain.doFilter(request, response)
    }
  }

  def destroy () = {
    println("destroy")
  }
}

アプリケーション起動時に init がよばれ、リクエストごとに doFilter がよばれてくるので、doFilter で request, response を適当にいじくればいいことになります。ここで chain.doFilter(request, response) をすればデフォルトのハンドラが呼ばれるっぽいので、普通は war 以下の static なものがそのまま返されます。

throught() は GAE/J のテストログインハンドラのためのやつです。(/_login? とかを GAE/J 側がハンドルしてる)